From 4ad12a680c03deb0c4d9ff6065831dad036de312 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Devernay?= Date: Fri, 26 Jul 2013 18:39:03 +0200 Subject: [PATCH 01/68] fix cap_qtkit.mm for multithreaded applications cap_qtkit does not work when the capture is run outside of the main thread. If the capture is launched in a separate thread, then [NSRunLoop currentRunLoop] is not the same as in the main thread, and has no timer. see https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/F oundation/Classes/nsrunloop_Class/Reference/Reference.html "If no input sources or timers are attached to the run loop, this method exits immediately" Using usleep() (which I previously proposed, and was reverted) is not a good alternative, because it may block the GUI. Here is the new proposed solution: - create a dummy timer so that runUntilDate does not exit immediately - simplify the loop by using runUntilDate instead of runMode:beforeDate - fix potential memory leaks (pointed out by Xcode's static analysis) - fix init to follow Objective-C guidelines - fax warnings about conversions from size_t to int --- modules/highgui/src/cap_qtkit.mm | 91 +++++++++++++++++++------------- 1 file changed, 53 insertions(+), 38 deletions(-) diff --git a/modules/highgui/src/cap_qtkit.mm b/modules/highgui/src/cap_qtkit.mm index c7afffa075..2335e5c215 100644 --- a/modules/highgui/src/cap_qtkit.mm +++ b/modules/highgui/src/cap_qtkit.mm @@ -277,11 +277,17 @@ bool CvCaptureCAM::grabFrame(double timeOut) { double sleepTime = 0.005; double total = 0; - NSDate *loopUntil = [NSDate dateWithTimeIntervalSinceNow:sleepTime]; - while (![capture updateImage] && (total += sleepTime)<=timeOut && - [[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode - beforeDate:loopUntil]) - loopUntil = [NSDate dateWithTimeIntervalSinceNow:sleepTime]; + // If the capture is launched in a separate thread, then + // [NSRunLoop currentRunLoop] is not the same as in the main thread, and has no timer. + //see https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/nsrunloop_Class/Reference/Reference.html + // "If no input sources or timers are attached to the run loop, this + // method exits immediately" + // using usleep() is not a good alternative, because it may block the GUI. + // Create a dummy timer so that runUntilDate does not exit immediately: + [NSTimer scheduledTimerWithTimeInterval:100 target:nil selector:@selector(doFireTimer:) userInfo:nil repeats:YES]; + while (![capture updateImage] && (total += sleepTime)<=timeOut) { + [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:sleepTime]]; + } [localpool drain]; @@ -326,9 +332,11 @@ int CvCaptureCAM::startCaptureDevice(int cameraNum) { } if (cameraNum >= 0) { - int nCameras = [devices count]; - if( cameraNum < 0 || cameraNum >= nCameras ) + NSUInteger nCameras = [devices count]; + if( cameraNum < 0 || cameraNum >= nCameras ) { + [localpool drain]; return 0; + } device = [devices objectAtIndex:cameraNum] ; } else { device = [QTCaptureDevice defaultInputDeviceWithMediaType:QTMediaTypeVideo] ; @@ -392,6 +400,7 @@ int CvCaptureCAM::startCaptureDevice(int cameraNum) { grabFrame(60); + [localpool drain]; return 1; } @@ -415,6 +424,7 @@ void CvCaptureCAM::setWidthHeight() { double CvCaptureCAM::getProperty(int property_id){ + int retval; NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init]; NSArray* connections = [mCaptureDeviceInput connections]; @@ -424,15 +434,18 @@ double CvCaptureCAM::getProperty(int property_id){ int width=s1.width, height=s1.height; switch (property_id) { case CV_CAP_PROP_FRAME_WIDTH: - return width; + retval = width; + break; case CV_CAP_PROP_FRAME_HEIGHT: - return height; + retval = height; + break; default: - return 0; + retval = 0; + break; } [localpool drain]; - + return retval; } bool CvCaptureCAM::setProperty(int property_id, double value) { @@ -480,13 +493,15 @@ bool CvCaptureCAM::setProperty(int property_id, double value) { @implementation CaptureDelegate - (id)init { - [super init]; - newFrame = 0; - imagedata = NULL; - bgr_imagedata = NULL; - currSize = 0; - image = NULL; - bgr_image = NULL; + self = [super init]; + if (self) { + newFrame = 0; + imagedata = NULL; + bgr_imagedata = NULL; + currSize = 0; + image = NULL; + bgr_image = NULL; + } return self; } @@ -561,26 +576,26 @@ didDropVideoFrameWithSampleBuffer:(QTSampleBuffer *)sampleBuffer memcpy(imagedata, baseaddress, currSize); if (image == NULL) { - image = cvCreateImageHeader(cvSize(width,height), IPL_DEPTH_8U, 4); + image = cvCreateImageHeader(cvSize((int)width,(int)height), IPL_DEPTH_8U, 4); } - image->width =width; - image->height = height; + image->width = (int)width; + image->height = (int)height; image->nChannels = 4; image->depth = IPL_DEPTH_8U; - image->widthStep = rowBytes; + image->widthStep = (int)rowBytes; image->imageData = imagedata; - image->imageSize = currSize; + image->imageSize = (int)currSize; if (bgr_image == NULL) { - bgr_image = cvCreateImageHeader(cvSize(width,height), IPL_DEPTH_8U, 3); + bgr_image = cvCreateImageHeader(cvSize((int)width,(int)height), IPL_DEPTH_8U, 3); } - bgr_image->width =width; - bgr_image->height = height; + bgr_image->width = (int)width; + bgr_image->height = (int)height; bgr_image->nChannels = 3; bgr_image->depth = IPL_DEPTH_8U; - bgr_image->widthStep = rowBytes; + bgr_image->widthStep = (int)rowBytes; bgr_image->imageData = bgr_imagedata; - bgr_image->imageSize = currSize; + bgr_image->imageSize = (int)currSize; cvCvtColor(image, bgr_image, CV_BGRA2BGR); @@ -734,29 +749,29 @@ IplImage* CvCaptureFile::retrieveFramePixelBuffer() { } if (image == NULL) { - image = cvCreateImageHeader(cvSize(width,height), IPL_DEPTH_8U, 4); + image = cvCreateImageHeader(cvSize((int)width,(int)height), IPL_DEPTH_8U, 4); } - image->width =width; - image->height = height; + image->width = (int)width; + image->height = (int)height; image->nChannels = 4; image->depth = IPL_DEPTH_8U; - image->widthStep = rowBytes; + image->widthStep = (int)rowBytes; image->imageData = imagedata; - image->imageSize = currSize; + image->imageSize = (int)currSize; if (bgr_image == NULL) { - bgr_image = cvCreateImageHeader(cvSize(width,height), IPL_DEPTH_8U, 3); + bgr_image = cvCreateImageHeader(cvSize((int)width,(int)height), IPL_DEPTH_8U, 3); } - bgr_image->width =width; - bgr_image->height = height; + bgr_image->width = (int)width; + bgr_image->height = (int)height; bgr_image->nChannels = 3; bgr_image->depth = IPL_DEPTH_8U; - bgr_image->widthStep = rowBytes; + bgr_image->widthStep = (int)rowBytes; bgr_image->imageData = bgr_imagedata; - bgr_image->imageSize = currSize; + bgr_image->imageSize = (int)currSize; cvCvtColor(image, bgr_image,CV_BGRA2BGR); From 6be8757e8b93ca7593de76f66d2479737eb69c1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Devernay?= Date: Fri, 26 Jul 2013 21:32:35 +0200 Subject: [PATCH 02/68] fix signedness error OpenCV's automatic builds don't care if you store an unsigned int into an int, but they don't want you to compare signed with unsigned. Does that make sense? --- modules/highgui/src/cap_qtkit.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/highgui/src/cap_qtkit.mm b/modules/highgui/src/cap_qtkit.mm index 2335e5c215..bde200e410 100644 --- a/modules/highgui/src/cap_qtkit.mm +++ b/modules/highgui/src/cap_qtkit.mm @@ -333,7 +333,7 @@ int CvCaptureCAM::startCaptureDevice(int cameraNum) { if (cameraNum >= 0) { NSUInteger nCameras = [devices count]; - if( cameraNum < 0 || cameraNum >= nCameras ) { + if( (NSUInteger)cameraNum >= nCameras ) { [localpool drain]; return 0; } From 9a560a5a8d89b92b79cea3a04028e38c079c40a9 Mon Sep 17 00:00:00 2001 From: Baris Evrim Demiroz Date: Tue, 27 Aug 2013 22:42:03 +0300 Subject: [PATCH 03/68] Added a new tutorial "Using OpenCV Java with Eclipse". Removed information related to Eclipse from "Desktop Java" tutorial. --- doc/tutorials/definitions/tocDefinitions.rst | 1 + .../images/eclipse_main_class.png | Bin 43310 -> 0 bytes .../images/eclipse_new_java_prj.png | Bin 12275 -> 0 bytes .../desktop_java/images/eclipse_run.png | Bin 75383 -> 0 bytes .../desktop_java/images/eclipse_user_lib.png | Bin 43133 -> 0 bytes .../desktop_java/images/eclipse_user_lib2.png | Bin 25561 -> 0 bytes .../desktop_java/images/eclipse_user_lib3.png | Bin 25823 -> 0 bytes .../desktop_java/images/eclipse_user_lib4.png | Bin 33279 -> 0 bytes .../desktop_java/images/eclipse_user_lib5.png | Bin 13829 -> 0 bytes .../desktop_java/images/eclipse_user_lib6.png | Bin 35112 -> 0 bytes .../desktop_java/images/eclipse_user_lib7.png | Bin 39352 -> 0 bytes .../desktop_java/images/eclipse_user_lib8.png | Bin 25442 -> 0 bytes .../desktop_java/java_dev_intro.rst | 98 +--------------- .../images/1-window-preferences.png | Bin 0 -> 13557 bytes .../images/10-new-project-created.png | Bin 0 -> 42297 bytes .../java_eclipse/images/11-the-code.png | Bin 0 -> 34150 bytes .../images/2-user-library-new.png | Bin 0 -> 48175 bytes .../java_eclipse/images/3-library-name.png | Bin 0 -> 18880 bytes .../images/4-add-external-jars.png | Bin 0 -> 53945 bytes .../java_eclipse/images/5-native-library.png | Bin 0 -> 50587 bytes .../java_eclipse/images/6-external-folder.png | Bin 0 -> 29227 bytes .../images/7-user-library-final.png | Bin 0 -> 47479 bytes .../images/7_5-new-java-project.png | Bin 0 -> 23680 bytes .../java_eclipse/images/8-add-library.png | Bin 0 -> 42696 bytes .../java_eclipse/images/9-select-user-lib.png | Bin 0 -> 35428 bytes .../java_eclipse/java_eclipse.rst | 110 ++++++++++++++++++ .../images/eclipse-logo.png | Bin 0 -> 6162 bytes .../table_of_content_introduction.rst | 19 +++ 28 files changed, 133 insertions(+), 95 deletions(-) delete mode 100644 doc/tutorials/introduction/desktop_java/images/eclipse_main_class.png delete mode 100644 doc/tutorials/introduction/desktop_java/images/eclipse_new_java_prj.png delete mode 100644 doc/tutorials/introduction/desktop_java/images/eclipse_run.png delete mode 100644 doc/tutorials/introduction/desktop_java/images/eclipse_user_lib.png delete mode 100644 doc/tutorials/introduction/desktop_java/images/eclipse_user_lib2.png delete mode 100644 doc/tutorials/introduction/desktop_java/images/eclipse_user_lib3.png delete mode 100644 doc/tutorials/introduction/desktop_java/images/eclipse_user_lib4.png delete mode 100644 doc/tutorials/introduction/desktop_java/images/eclipse_user_lib5.png delete mode 100644 doc/tutorials/introduction/desktop_java/images/eclipse_user_lib6.png delete mode 100644 doc/tutorials/introduction/desktop_java/images/eclipse_user_lib7.png delete mode 100644 doc/tutorials/introduction/desktop_java/images/eclipse_user_lib8.png create mode 100644 doc/tutorials/introduction/java_eclipse/images/1-window-preferences.png create mode 100644 doc/tutorials/introduction/java_eclipse/images/10-new-project-created.png create mode 100644 doc/tutorials/introduction/java_eclipse/images/11-the-code.png create mode 100644 doc/tutorials/introduction/java_eclipse/images/2-user-library-new.png create mode 100644 doc/tutorials/introduction/java_eclipse/images/3-library-name.png create mode 100644 doc/tutorials/introduction/java_eclipse/images/4-add-external-jars.png create mode 100644 doc/tutorials/introduction/java_eclipse/images/5-native-library.png create mode 100644 doc/tutorials/introduction/java_eclipse/images/6-external-folder.png create mode 100644 doc/tutorials/introduction/java_eclipse/images/7-user-library-final.png create mode 100644 doc/tutorials/introduction/java_eclipse/images/7_5-new-java-project.png create mode 100644 doc/tutorials/introduction/java_eclipse/images/8-add-library.png create mode 100644 doc/tutorials/introduction/java_eclipse/images/9-select-user-lib.png create mode 100644 doc/tutorials/introduction/java_eclipse/java_eclipse.rst create mode 100644 doc/tutorials/introduction/table_of_content_introduction/images/eclipse-logo.png diff --git a/doc/tutorials/definitions/tocDefinitions.rst b/doc/tutorials/definitions/tocDefinitions.rst index 4695623cca..d1fb039702 100644 --- a/doc/tutorials/definitions/tocDefinitions.rst +++ b/doc/tutorials/definitions/tocDefinitions.rst @@ -11,3 +11,4 @@ .. |Author_EricCh| unicode:: Eric U+0020 Christiansen .. |Author_AndreyP| unicode:: Andrey U+0020 Pavlenko .. |Author_AlexS| unicode:: Alexander U+0020 Smorkalov +.. |Author_BarisD| unicode:: Bar U+0131 U+015F U+0020 Evrim U+0020 Demir U+00F6 z \ No newline at end of file diff --git a/doc/tutorials/introduction/desktop_java/images/eclipse_main_class.png b/doc/tutorials/introduction/desktop_java/images/eclipse_main_class.png deleted file mode 100644 index 84c152e6da576ba8ec604081a11764f64f279559..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 43310 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Ė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 diff --git a/doc/tutorials/introduction/desktop_java/images/eclipse_new_java_prj.png b/doc/tutorials/introduction/desktop_java/images/eclipse_new_java_prj.png deleted file mode 100644 index 34e03972e778a211b15987abb30c3ee5dfdc362b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12275 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 diff --git a/doc/tutorials/introduction/desktop_java/images/eclipse_run.png b/doc/tutorials/introduction/desktop_java/images/eclipse_run.png deleted file mode 100644 index fee34afa10c4694da449ac12e2f85b5a1da2cb0e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 75383 zcmZs?1yEc~&^C&@B?J#1LU4Br7A#2ch2XFRcXtT{C%A72?y|VMJ1p+7xVtWwyx;fV zy7$&SRa;YMPMtHe(>>i!cRv%Rq9lWjL5cwf2Zt>uE2Rnthe!hlhoFas@Y*BJtX}lm zz&okRNWhhkz2ASmK{6Lt6o-SWiotv`LVmqRx0eMt!NK8l|NFuB+ZCF=c9J?vYdfpi z0iE599L?ZVj4W)OS(T*Ksa&~Od008v*{>Y4;ox5U2<|?OHSa!X*O>uYs+k$*|Jsl*VI)~A)X>O5(G)akilpQdhmL6r4}M?&+L`sg?o=H6V~R8rDhvUP<8L?N?snpe|1UxKWF7BuuS&!)&s z`l84s>TBHiLi^Jen~zJ&n&p5|HY@sN z*<}&hR)v~tiv(Y+_KRgC|BXT=h1}(lit)@iRNluscUFF4HQss_ClGk$`WeCIly-CU z@SaHj;Gl8GUFh)W#N#|qt0@Tf=y`Kw2+nxaJWIUN>n?zKyJ@-Met!4o9UO$ru7$&F zD8b`$hwjN|V3@6yd)wgUS&Y!Pxuv*@wz-t9wQ{hT2l%d7b0#GloF7TUra7OG&3Bqu zRr-?J^-)GhZ$1|s*f)(FWx<=Ymhy4F=;@0seFb-5X2aCyBggLZ;cI%d+;>MYl)dX~IZh|ARns@0aM$L6k8t{_7%F00hz!BT41fO*^2 z<0cjGVT-ZQ_?$`!%96v_kHOvI`0)el#|6tE8zEgq=59lcCC+zCY2VDu_ce4img4k( zhG6_bDDu$vOM8oWZQqk)CV@J)V~Lfm-(Hw`NxI<13F0jk zgftr3c`V1(psT^>^5nC3$rEKGov0|Sk)-(@vb|bNwj`P^T2fStAD5txY8bn5G^bxU z)RfZhA)-B*bls~Prnl167CGOs)F##I{E4+cG`)gA zn}bmJ`4dLNHj9Y$z6TT1eBkiMT^6$Wz{AQ5vUwz3C^oqt{D|cYJJY*)$1dhRUBRg- zBf{7u9&3;8(u#K@7{N7g;5H@iHpWW{*n`Fh##{yCDqP*>K?s2vnWnn6_P5&K+Q)qa zE;FRUq>8m~OI^2yildmA_{vM?E32)$ho-QZBKS&~b=sTkpAWn(%?gx6&glaIkvVXM za3v|qOdA`alqje~9m!gKs@_U74!wKwABr#O6b*?wwJSgX9x>=;^HG7Jf;avBYh>Tq^v$&=udX2PID zLDHGpniR|sAA$zdVID(x-)vOOXV*eFMm!Yc?{S1r2w3>mVOcDrjda)6Rq&|rb^JhR z(ky33AgEoeRXF^`Vo}LE zugiAyvmzGh?QAJPvla+|k`BbqLmdlzz`@cJx@NGKqQFaC;Ai zLxd}VE2r?>%ML!QXr~By+Zs*qwvQ8@P?AYPi}9OY%OX}1vgRz7wDuw-$-31{&jx*n zRo~{^I&YN7CVQg8kEZ)o-}3Xt>CZ^0*(DlC?IzEqc;jTfbtblA@a&RyhAsr!}F-_5cOn6pR>Qwzbh>(T=(jcY;3`u(GlBL?V$>CNF0bW?_CB;SfzL0nZ zrmI=nNQ$>0LRyv(fC_xKY#zJdU?{^eFUAmAgT_p)_rtD~>6p2AIlth}GFF_SP!0 z1b;Ug?(`yafeWFmA6(5gMxK6hMG%fL>+^%1H%6*|axoH)6@KT>u{3(iRV6i_bwBnP zg2(ew4sEc4H3J?%z?PxWV6O@50wfcS4e9YSnRk{lTM`kCFv>5yoMqNWq#)-a8NgSe z0Xv<3-bA|GSuX^7c&#dAh+MU=w!egEg*?wap3;x$^V^MAU7EpZd)uoZF@MoYDABoO z)MZm^RyE^9dlxB<*>myddxhp|oQz_RC>c^P@4?p?XQY|J2%O4DY9jvDj!ER4lJ|8} z7^F%ZAj0%_HZj1{9h7RO$?TQLf#sn%GlPv47uOp{us4m#^Rzxlc1nIE~(St9Ohf7Nf|DJ4x zO*2}ikGxqnoc+mUh7yD&_VNgdh<-2l4BftBqc`^!xIapcXZs4u5O|(0-3RHXi89zd zt2!} zp3PeTdX%ij*_IIpgC-0M#Zxw2C({7oHQ~QEc9puMk41WmT3lTku)vJB0$41F#@}Up z72q>}8Va@ZKBa#ZIY}fELX3{a{8;X7tMW@ z4dT7{v~&Kh0F{C%bssF(1FOR9I|Ndk5jicr3?G;s%>nLK_ z>QGW9NftF*Ua;62Q+`BZwpyf57JdA13sz?JgUP{mLGOHEavZg)ko@-ZI+9Z=q%OdF zLdv%ij+Ngl$7Qns(|uVW-kar`D)WL!We_LqD&f2!KXQDf?b@!LbR88uYa@EuXEOao zgvV(zS8-T$t?6m_c+DrVN&`4lp)$)`0NYH*VE)Y*BSEmzl&|b94 z+!h-0OPjq}V(<^~G%qW34&jqnHHPp&(!D{ zWz3NFKTz~qzePs9A*Xzyo*z7?%#V6G8|lI2`WP)JOHMK6t;@pk%KvKYC> z*?ysfWpAL*^*EpVn^I@AjW^ofvS@>ytO1HN9ui}*eh)p9=6!w%ys(`3Eipk+SdM}& zFASPbCTudh0fwHoUAb(6=-WI`gMZ`t-Au-V`$UDzkHAi1zLqyaxPIr`6>IHV1{7^j zgO>}~Fj!fyROLzAIU$y7Hd;a43YU;WeO^- zleQvsu{&!_y0V}(k&M{0AY9iV$%wBIdA2|gop8wrmUB4#jfG`qZAzw}#!1hr+O}CV z&fk%knEmrW+Z29Umey|={V{r)5VG8vGMu&-(taMO$uj~oM;R7lN9?i za$Zj%tZ)=-Oyn3bnnLGd#W5n7nA&dK&tci$sd=J)RJ*ZtWM2B>bk_iBj;?rPa~noW zA`sRsW6+}71iVEL61Rn3;qn^J=|FnyDF*nWf!mRdCoh7xD{(xxFSgIm?Mr7Jr@Ufk z%{I7CtoH$HCl7~qw=kE9fOgo<*_yA@cIR4#`x##W<&)w=P`f}$JwuFWSA=1n#j`D)8$E#-OA&=?yb-H11P`EeRn<-73<}?UF-t<;yXuK?q#{K`OXlH zmqdY#snnJ>g=aswOPj}ruJ#y3ts`P1nUXf;x}N8!>(v(yDf%jD5eMpG$6R7PCA-z)Y%PR3onYkQj3$81a4$Q}CGc3x*Y!M!cDZfE5ov&7dWEv*d)sr$k2N|<1rd*E zBSHX7uwbl`vuK=cWjcv_UsxoKIhJFXg2npZLK@oEP_z2|_8yH~%RBi3a zw~B}KUX6dX6ZVFTy*|wF-S2+6_RG+F7#$K@soGI?D{Xy@#&%n)x<3)BTlIO2_Oe^^ zIkkB_C3in-3kZb?pUs0%vmP!xru`^VSm(FfUow-2<&6mZ?J1726x{=N;O=vm`RvzP z5vk1wDCHO2Z^;i&Vb_bzeyuyouR-2@g3!z3%QP2oG_A&HYRT!=PQcUsaUWl3Jnwi= zxK`+L1a{;U>ZmrG`JKnBe-1y%@1jqO7L;0ZHq3I6z&x6Wj8=WAwmHgJoLyd8sSV#* z3PE-q$#n-K6(Y=~S@LeBY_i&Xj>nNXNl7^ilB1uW$Q6XiwgCL}635%RRJP6uVm@H4 zIwS139D`6&YV(ff_kx2_+u9}iu=fS^&yJ`g$-0WK7O!4CR!=%|IkO-KCZVfa*7k`n zPSV+36>dk@d8hWvarCv!YhHi}S=giQYYWte+(UQXIHlHQ?cC!KcWmts*B70w{usaS zH>ewxM4R5=d%XG0(a=;M1r`A)lA)%V2|ZRyB~g_2Z^G`Xf|nL}_6WEN}-Tp>2ml^zThQu^~bl@RFH5b z)H1rosEFnAy+=wNa*9iPIN-AmQXP^>E{{+K~G0U<$o&>Q8%c3*h5t;3~#tx|w zV>Z$%M=K4+6c=c-O@G^x(vepuoV>jCtGMNfP2X8VqPIfFwp*TN3rB{vjJzOY_#>KX zL3aXmes}@B?~d&~$9@oVf3LDWHaOLJT-PI`XtCM~a%j$H?!)iO6 zSZ|JgZRWe(GCl8Z3n=e>9%$Q5J|@WuYsZKI(Tpa?gfAY|VxC|<-5*c~7+ucv9xcX% zP}XXF*cmY8gB`Q4;H5M_>vv zyr8AVqxqrz=H#yn*Uwt0qTZfK4i`e?Dzquccxs9vmfjKRpM2mC#||w+!W6(cc}V!70N;0=JD?w|-^Egi+`@M>9_p z8)<&yYAN`ILmKC@G5F5C8c(>F`Cyc$yfoshkEvSF{wo}ze)Fp|;Rt@=A|sH=IrByS z@GmQQ2aZ!y9$m9XvxWyprNJw7&izb(r(3hn&UMf)h}HM3oyKQ1yrCC5mBJU%LapVq zh2~^M(uYR^%xqCPdyZCHqX*G|gp|ruj<0`j9q$6ggEg9!NRDK`=x;7j>bH2iT$ii$ zxE65ef`yKQ9Hx)Cg3+wp3P_Ij7FxcEJO0*MzXV^y&AWdgE1=Rp z5rmMJEGczIG7)d)rX->)HHm#@g>uN5!Tp|uz<(~3Nj7CvTVME-T`f#J0l^hr(wF8q zL{$@ekNcI^MvVjzmxV>z_0FC1RSLnV`fjrHK!%iF!qLDmz~d*ApNN$bKKB>;vA9oeZY#va5f;Q)hZ; zG^rX5dS>cprYf^OhBijl@LR(JMPSP%{o^}jEAds9ZV#57vqwt1C|)+!E2~(O(V4@_ z?>Rm`%HKbn^@RHs;=|s>)Nk+qLZ`cFNg5G-hyQ1 zylJi}V<;c`w`5+<@51~#5on*J_J_vW9={I zRwf`CCUwR@jk3SF;3PGpYnQLZb`xE76TNwbMz7%ZitL}0ePV&jJ}1lsBi|008&D>u zCDq=qHxe`}v8=czEOLz2QR?Q`LkIgi+7v`>D^KHNg34CXlf1W|HnI+>N!FRQ8 z{ZK*;I}!J9b^u*mppBE!60TWTl55rWR=S_3MSqTQ;kkP26emjrx=`yR^N|f^I>$$Os?802yj8n7 z*7kNh?7~-|KheLu|DABk=FRUsmIm#oQ(G@l^1cWPj#1M!f;+KuhQpcvL4-wH{aeT^ z9c%RsaEJz6JK~UwzxxyYw3toa%Q{wRM72QochO1Fx_ugoq_1~QY;dfHx@nxyf~K&> zh@wk4!Yl*l57|vLaK)YV?d*1#*xemm`hl-8mK3e?YuqVSQA2;hC?dS38Vqd|m#c9waA z7ZP)ZKg8~YX>aOZX(tFFd-V4$ugz=K$KxQ7C+DNL!;=I&by+5T*s-yzWu9SQRpUy5 z=5fe5fA8#(K(F=V#$`b@hFIDaRBNr2rO;9!R+?RbmIdFslk+QRR8MpRoNCURcq)(x z@;MMJ~tF153O{gWPGs zwM1dj2HPh~nHT5H44{_uhrt0!6B!B;p^N#u-}L=4!aPQ%4?Vxl>7)5y_J55G(pKkV zp$?~BXqFQVr(F={4rK^GJ#D|){TMAv|K&BviHV}?B~LqpO}-`@%Cws-PXf`EvDG*f zs9Csx`^1>+hXeQF4779JPj~g0V)Io-eZLjKVaNJeu0Vs;K|KL7bIO(0?WY!wbTL1> zm3;+`2W0XKA~1tmoIeJ*|8(OGSjy7z6@+?Z_-=`m(NoQJIczS zUmpUW*TFW){FJ5z~()3|z^>Cv}`NNN&}U^iLt8_AnUY5;45h@pE`lH#E8 zsaKtc2F{p$|0DeCcK(C#`x_N!rOSr`oYl9_>#;-uH8>zazp6q_OMrB3Pk2<4-uDu4 z++t3_xF7%iaJ$$&P0Js={G!U+?KGo^(FjyTxCZ;i?a2ZI*xfW+OrAl@_t_M+#aJeA z%M{9JIqx>Q>%C=Arzr6wz{aFmAR{<|hqHOJSM>3DlpvWE`q8c}qvc-yBL9iJ2gA*p zaj~k%ov5l>gsg+{#=0<96S_i0@WK54`$}tosluhX8T3?6wAN+%f`p5*WuGYdLcs{Y zbTDe1@3o**Unv;|j|W$0BQu+_+xE=t0I9pDS2}5MzFALHu;`@|&`1ppzwt?KG@UAU z2wOW|bV7#JS9dT=WPUhqHi7J-MKZRab@@|JWVI-gP;)?>aFrsXT? z5fPcG%o?*-Je7N`MOR-4>Aqw#f3{@5A(evR99c8s7%>yGGUuJfYU)!dCJiHK%~lON zH!?p2lJkB?Q;0hmETscibN6BN1VJbNxx&AX82{Bu;}P_YhHXDg8b^D}1Lg69gB24yvXQS5OiPD zJtDdpHA*{>?`=AKCWD?SD%TUSyN#VQ?m@5lY}=y^qj53gYMD?G%J9~+%a{9G5aZ3+ zIBd6j?xjCx?xjM;50=w!dpEh&emCBa`u(=YAB1(kCPv~oJstwPFjt0MJkZ<01Zv5$ zn(k5V)Q_}=`X7ovG4M7xypr^>GIhmlQBMEP20=Sw$IQjQ1?Li(~nyRy`)t_!G|E9I&;^`ZOu zZx*8V^lH1VO9fFczvr=)H=}|`Ln(57yoBqG4U#jdE7Nn>+3sab z0p_*{Hr1rw)qJwxkiltjk@0Ax_6Xo6aR7U`MUArZoyOnXy!{alSo+=$OKn9;YfXzK z^P=YFJJaGAHvXb-Ad3W#VrJEbPfw^Dlc4NX;LEJtBW_yiW4a?`#LP)~7#LjDg8RlZ zo5|TgKu?2~i#;IkwQl?wi(wtT&fC2X3-?3#sj}KyRH2tv%$>L3vZ5gjovUuX_KOY? zx`o3$XEgO<`JJIJK8@cocgq-3ey4w@aqP08Ef(J&5pd_`@3cw29RJS6m1q1j*uErm5&D(=g0I5@*KUtfrzOozb!t+Oq-ICqgc@ zra(p6$SvTl2^cD+WieHP;!rEmf>^qA4ZFV6{dPI3$2PG)X?I+UYoJEJXV%Zt{#TvC z5MVen?fOVA_N~Db#fFa4mg3Bdt~24GQz1OtL=FbcnIBg_YibA-?$HFB>8 z25=#8Z~CWg@%B?`ksTD$VSOhJ7~Hns9O?OT-m_qdAsm*|a*vdi!V$dr&Lysm0u^hg z5}%ez0~f`qOe!`Mr~iR+#MBhOa()=2a~_ZS(r_P6!K_pz1Z^YIg_y2~kbPWDYR_{Juc{U;!yP+sh8ogE5M1M=%z&|tg|3Ds-JBh(fy=O zH{xIDcrPv3o7_jGY)caxUZ`LWVc5>?h+lpA@W6{M0cGf82#0S*QN3~rb}mmeMjiA# zx$e*?TBsBbyWmE38mSi0_Wq@EZV2G7!YW9z_%$(%<(6A=W}uOCrp2R){yMXV?*WGH z3Uv=bM#vufNFH@C8evT{E~DY`nY#=@2e@xn)*SVkpVVqQej8;Go$}LC&>-#2az4}u zD?u(I0G{S)^Avd!2bH7-*%55QrwtA$J7G(U7TN-tsq3!tLG^6uiZ8(k>0f2$if9xx z9PpRF4VjAE{6gYFQ58&j`#HRoIP|`-13aWf8Bj0EMXQ3$$^_X9&)H^>fKgGW@7nTU zW5Sn=S43+64;2VO{m-f4uu)f1ebG65W37}MiZ$vTlCgg-!4TYEa^S1stoUJHcM;;M z3`{on@%qUYG{+@ZTHgA-wz|ocDF~=vR$DW*$ZI>KP-?Ykh>j0bvV*r|DgI`_Q)X}; z(T?@48h$wPTYPy8BT`)l#;0Xgt-5&Rj zhrjw4)r7{|nV*_!duoXm_D?-)ZF;v?deRbS>vTz%Ez1<$g39gKxKf{3*;=p35JYIg zvBw*zX@2Awr8Uz5#b@l=3~o&~Wx4+k+39~cPcq1G8CbbyVSvvzu`9J!tv_Cw5C8Dh z|2EMWaQyJ1VK^+PBL|JHWzDGnAHtLp{O6D3DUb!o^x|sNf7G4-`*p({II4K_5zEsf zTkCzP{|He3OS6I_sQd;OJIDpBt#zB@as8A1e@6Rn(D_er1KT9vG#$a=5?#f*^Hikc z)7h}yA?2^lJR8yH^i&^@jLEW7SYOOFNvc&NCNQqGl6)8>vP$$nl(pI{R>hJ+E8_Ak zV~blF{@PjnO;qCXcFnPGXm$2ii7`Zrt2QCcP0=Sku~%vo4Wgm`b2$B>MznEr0cqpw zTm(8Q=0i$oUx+*N@)eWQ8{VH^{)pNhL^}MlTP+x`uCJT9>FSf$RAp5oxTd<-lnf-_ zV6yDs#2-9=6xVnAJFF4S?QaTW zoo)!izWs&!y?aSvOuNZObsW4I1tEuIX?oJrS+LMs+f3K_L>Q}!$>bAc0&1~Y^s#!t z9!C`Xt#Kw6F@fE_OW=k8X=AB+_;&%O`oOh%bVNZ(tQa(?m~kiQUr|~WE>^CSxh_~p zuVCdWH@m4tsJk@$U>38H=cDekdza1WDMzT}uTfSDZ_3n^h%>Fi6vht1`;y3b{)jAv zJ4o(voXJK4dyCu;6C%ZjJk9zuZUYPH r(BY1~9)qEk24bKpE1cIEX#hmNE$CRr# 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 V9HJcy`wR^H>1i2hmZ)Pw{sUO}%W(hz diff --git a/doc/tutorials/introduction/desktop_java/images/eclipse_user_lib2.png b/doc/tutorials/introduction/desktop_java/images/eclipse_user_lib2.png deleted file mode 100644 index 2b9ec5c3c15c9b94b910bed7bd9e5b831f23f4e3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25561 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 diff --git a/doc/tutorials/introduction/desktop_java/images/eclipse_user_lib3.png b/doc/tutorials/introduction/desktop_java/images/eclipse_user_lib3.png deleted file mode 100644 index 4bf83ee033bcf376c36ff5fdee4d9f67f4e16615..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25823 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 LeYH}Rn?e5%dF7t2 diff --git a/doc/tutorials/introduction/desktop_java/images/eclipse_user_lib5.png b/doc/tutorials/introduction/desktop_java/images/eclipse_user_lib5.png deleted file mode 100644 index ed79d92d418977e3e120587acd701aaa2db9da6e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13829 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 diff --git a/doc/tutorials/introduction/desktop_java/images/eclipse_user_lib6.png b/doc/tutorials/introduction/desktop_java/images/eclipse_user_lib6.png deleted file mode 100644 index 3a98e38b1bdc2c3940cfbdb4eea7ed060e3167ea..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 35112 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 diff --git a/doc/tutorials/introduction/desktop_java/images/eclipse_user_lib7.png b/doc/tutorials/introduction/desktop_java/images/eclipse_user_lib7.png deleted file mode 100644 index 019432016a23027a441cf0bf34b25e85ff7f450e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 39352 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 W1q8J#7K3+yKm7~FI(V(?;r|9BhU~xq diff --git a/doc/tutorials/introduction/desktop_java/images/eclipse_user_lib8.png b/doc/tutorials/introduction/desktop_java/images/eclipse_user_lib8.png deleted file mode 100644 index 5650aa79a436c9a875945b957e7662b0282d3651..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25442 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 diff --git a/doc/tutorials/introduction/desktop_java/java_dev_intro.rst b/doc/tutorials/introduction/desktop_java/java_dev_intro.rst index 1b20bec253..d5cb31f894 100644 --- a/doc/tutorials/introduction/desktop_java/java_dev_intro.rst +++ b/doc/tutorials/introduction/desktop_java/java_dev_intro.rst @@ -7,10 +7,9 @@ Introduction to Java Development As of OpenCV 2.4.4, OpenCV supports desktop Java development using nearly the same interface as for Android development. This guide will help you to create your first Java (or Scala) application using OpenCV. -We will use either `Eclipse `_, `Apache Ant `_ or the -`Simple Build Tool (SBT) `_ to build the application. +We will use either `Apache Ant `_ or `Simple Build Tool (SBT) `_ to build the application. -For further reading after this guide, look at the :ref:`Android_Dev_Intro` tutorials. +If you want to use Eclipse head to :ref:`Java_Eclipse`. For further reading after this guide, look at the :ref:`Android_Dev_Intro` tutorials. What we'll do in this guide =========================== @@ -19,7 +18,7 @@ In this guide, we will: * Get OpenCV with desktop Java support -* Create an ``Ant``, ``Eclipse`` or ``SBT`` project +* Create an ``Ant`` or ``SBT`` project * Write a simple OpenCV application in Java or Scala @@ -233,97 +232,6 @@ Java sample with Ant :alt: run app with Ant :align: center -Java project in Eclipse -======================= - -Now let's look at the possiblity of using OpenCV in Java when developing in Eclipse IDE. - -* Create a new Eclipse workspace -* Create a new Java project via :guilabel:`File --> New --> Java Project` - - .. image:: images/eclipse_new_java_prj.png - :alt: Eclipse: new Java project - :align: center - - Call it say "HelloCV". - -* Open :guilabel:`Java Build Path` tab on :guilabel:`Project Properties` dialog - and configure additional library (OpenCV) reference (jar and native library location): - - .. image:: images/eclipse_user_lib.png - :alt: Eclipse: external JAR - :align: center - - | - - .. image:: images/eclipse_user_lib2.png - :alt: Eclipse: external JAR - :align: center - - | - - .. image:: images/eclipse_user_lib3.png - :alt: Eclipse: external JAR - :align: center - - | - - .. image:: images/eclipse_user_lib4.png - :alt: Eclipse: external JAR - :align: center - - | - - .. image:: images/eclipse_user_lib5.png - :alt: Eclipse: external JAR - :align: center - - | - - .. image:: images/eclipse_user_lib6.png - :alt: Eclipse: external JAR - :align: center - - | - - .. image:: images/eclipse_user_lib7.png - :alt: Eclipse: external JAR - :align: center - - | - - .. image:: images/eclipse_user_lib8.png - :alt: Eclipse: external JAR - :align: center - - -* Add a new Java class (say ``Main``) containing the application entry: - - .. image:: images/eclipse_main_class.png - :alt: Eclipse: Main class - :align: center - -* Put some simple OpenCV calls there, e.g.: - - .. code-block:: java - - import org.opencv.core.Core; - import org.opencv.core.CvType; - import org.opencv.core.Mat; - - public class Main { - public static void main(String[] args) { - System.loadLibrary(Core.NATIVE_LIBRARY_NAME); - Mat m = Mat.eye(3, 3, CvType.CV_8UC1); - System.out.println("m = " + m.dump()); - } - } - -* Press :guilabel:`Run` button and find the identity matrix content in the Eclipse ``Console`` window. - - .. image:: images/eclipse_run.png - :alt: Eclipse: run - :align: center SBT project for Java and Scala ============================== diff --git a/doc/tutorials/introduction/java_eclipse/images/1-window-preferences.png b/doc/tutorials/introduction/java_eclipse/images/1-window-preferences.png new file mode 100644 index 0000000000000000000000000000000000000000..53b3631fd03e71e9c074c2fd45c3d7aa225e5a61 GIT binary patch literal 13557 zcmc(`byQqWw>F4Ff=jT*3GUXo1q~A1-K~+}PH?y2?gWBcBSC{Z!JXjl?$i9<_pX__ z_q*TBUGvBMaaONBRdr6)exCj8y{o#z6~9QKz9)PS1qFrrSz25f3JUri)K1IhsL9IhvW-Ia%5}>#HF_hH!#Di;Jqd>mO%XIOuA)?miyc9or=h8@SG@ zY(M|4wXo@l=j51nZ)>PqrPHuIUi>bhC?^wyMWy7ALp3ICVpUF{g;Oe`ghls#fS1eB zP-Ki3H~|gyISwk`UosHdFfK^&Hv4h)m(L?K2ui+CsN><6%WB(gmK*fbtNY5P4*v1p$SX<%C^{6S$1&^$eh74rx&^GDDscwCT**tv)*;HUy3>7MiZV#|=__AzsofY$;)Q`VV#Vg|xn}fvNm(#7)NXP`^To=1Ka}}@Otz$! zyXIXn-{zYhXNngsP%HEIxfIpM`hQ=N4Az!P(3Zm1k!%V{OHac}4-HCB-2fmqSN3sz zLofzBi2iX&DX@N|uwjvy)r~4?$3XjefC{U2g-?HqI;U1#+R9yCE`_3gC8?@!__GB= z%Lns=8f>hCJM`jjcX2fqr1VgYn{U5G%00MMc!ykCqROUST6)6y)Q$wSderuyd>F!G zE$eiDi1dbh36#nGFjZpQFm;BS5Go* z^TPgX-_m>&Zyz;B=TJzt?3uA)r%v_e&gy+h`dmYDLh}sGWtl=wDT=TLrj{m&mL{Pq z_)fL!9=q8hb~jrvUc(#4(XseJ)ksA=jj6bpCa+Yu*_NllgtNj)U_~hHBy|Ym!jwla zO>Qc;Tm(sHaA?A&-x)!JZ$QZQIQ`3E`j_L(FAefP<^=|cvkP4njDOMOjV4SSK(C2G ziPXsdyyIas95!UjOhSP^yej-8_N(9ewvb<7|I1C0mB9)Q{jk(SVih~l3CiO~`ZgPi zHe`~t`jV(TZ=km{Aqf<-( z55@;95`hQIk~8{Ya5E75--@eVi>o@SU5O_;C3zrA(tA>p<9`s0S8KxvEJ;UJYQQUK zHa=iJ-++0~CpB{wkn3S8uVBiR&=k=W$B_T~0~)VsEqmTju z7lT4EgrCClrB9sA7t+eT@jVd=2_hR47cP?L;YS-P_Yy`H$dt&)1rcMRIl^8KpsvAy zR{P6Kg=}8G3($V4lbTU)v{DlIt|fab`K3O_$Y$5F#YbR@ZP53ovXqfJwh}Z~UrD63 zf-Iw5nuS&&-pDAQ*5arG z4CS=YZ?4SWoQad~3E`+3_2uoF)2G?vrk8W(y{Yo{9d$RU6hYm+sSn71a$8nQw9nXh z+WBj10gM2plL%TTiA26M8P22@iecUO?G>e@6(u!6r5V9LTy1O(co0l_W)0wats*K8 z;d_y;#pkSl?#Fymb2=ZEQ0GY-%EHWLkE6_2r7ag%Ya*8ph?}Pg%NpzHTgPmN!|~>o z8w-0cpLDi2VJa1z;uf6blFeL`#c1~muD-ro*r~b%#BNWMj+P6i>py#FZ(9s*=TIjT zf2I}uuIR`^qB>$jJzP^CV9!3DIl_+4?Pq;IDIwl{|JX_9ka-Hsr5D zEM?AUk)GaH_JTBtv+J2-aZqlikyOYLVO21vSU;@fnia&GQK}C~QkVy>aBAwfR&}ag zv2I_}gnYO@&C~?fJAzI6}K$nv2&a8*4XL>Ag(e;Bb z23!zQ1;S4Q9AJq1v zembxwIdxGD%?DG+tVd8s1lbO3L^d1lewok4h$X`AtNM+pMTetiMq68p{6Xmtf&MI} z`l9d8rejm3%k#QugWWmO;Y!|gC6{UKa}Sw?AK@4w`IP4pp_>tC{kXnZD()XEk<9Md znNBmxTUG8o;)CzmiafqWDgx8-EnoMaV02Y-06whz;OZ3vr?+-ZHF${(BA8%KA+M zX^{A&5a?!H#-Gu;5p5k|+J%;BDWFbYz%c7oQ3e7+aNPVW=-(4YK~nk!Zr=`)y4?Uael`^7zT1gv1{nx`q<)W^=>&kmv_0IxeQ zVW=QlvbJqnd-i`zGTW5VUfQl#S2VCbEJ{q4{XB}NDL}Zc;j552fWV<*`q@I9yf9zz z2;?=N2A+b?Rd){(!$)B`3Zn)4;}tL*{rP)D$lAitUJ3XOHI9R|UQ@$gifNrfZDHKT z(G^G89#b}piTLwtkJ^&S&m*=Zxj!E!zzi*!kqgArgR0h3a1AN>E=hUw;rU`78Yr&a z;H0dQRC@UtG970#DI66qQwbeYEgN^M%l(bMgkV&fv1#8d9T&#BCri&kyi$oADqLSe zw;P9%wC+FfIpW)XSnq~5W94w&M}1oU%9$F8g0w`H)qEZ0^^Mi+V0sD!7>XIWi;r~W zw5;Sj)O5{El(U$kEhv}J7VrnZ#%#OB?z)NzINzI_1bqHz#MsZ4Zh{#7t(M$Sm)8{o zFz6B$aFJ;uSNWCt*p0P5AbX+mvpa6ee&&dSoc3{om0ki}R>B~7urOxuBi)Cu@(DF@ z@596eHC9Y)8`I~;qLQSMeMkbQ(idwgHdMy!4c+VwflDH02QHGsCaK-K zynj}|^rWTRJ2J$WC6hGZ`o$~XX>BwsZ2rZK_v7I^O`w&QsAL@8ucN};MK+Nc>($7X zuVj)g9ai``CR;udT{RHHG#=1O{nL({QtbS@C zi?SMv;z!Zz=(-@juf^0^A<>y3QCYy%S|w6i#MfVT_*s3j#4)qBl}tL!$ZO5?K(YO_ z8*U*S&BTM1g}A^&1n)5rpJ=n+!ftqa>V~5G6;0n2_1Xnp&k_}W%tR+I-MreuxEdgEK&SjbLuE zU}jr^yB=lHWsR+dwM;&o?Hyc*OgI~GhUV&;<`*in3PElUeOlu2&9XX5r@m8lHa`J zfU2JiyGlw)@0l{|xjRcE1eAiv|4<3mBeFbho1Ani>YbT8I^Nbe<(=ZyyiouIw*u{(5PvGZC_ zBKFc>y1!`Gt7`A9zwNASvp@Bex7^jA;4jgWvp@B|?i|xtTf857A@;hl_1cf7(Ra5! zbgBvoYHu~W&F;_f**~|?f7-Rs50WZr6nIn~I1yxpM+OUaxGf=9+4}8Y6MLxa=dp&yd?$M34S!YKtdG!^ zXVXt2&(a1h+<(ks?YVw!JB;nj+9GOEN$iC?c_a9@Xb#-gIc%143wfD}S&>^()AGGDiV>%{AG%n%6=sz8xW9kAIbLganj^#k zp@4sYQpL3s895r;bpqJgo%B8Tx2xKPNQki;5#Rw{2m2O=2}9t|A79OyT`$M5u7+0U zIv#e!9jzbIoFr@%cOzvw1a444$_v2otqw+&IXf0not$}0K#n)Ky1c!{2lZ!Z12T!x zzn?*nmZ}v14gx7i%dpT@*@M33#sq7Ey{QiRlBaYS(c6-WhzBu@@S+oB7M#Dz>1Tv+~tG1gnN<1DNazE~Qdo?N*) zNVWI+JT+T#H^LziopinOY4+Clq0{Sg>zBpXiGs(q$NkN9?}Mj9bb+fDHJzQ5-=qzU zH#pc`Z_|=lF~8rokNl5my|48bc5kHh?O6JKzi^uI%hPB=*YMYT;qs-KE!QX>S5j;9 z{yKNv6~zVH@Ywi5lkQ&_eWLkryoY^%tUu(3&p8qCw+%^sWM}glpsh;`C27?A+Bj(; z%PtF=S#3NnQd4`UstP+Uog(Y85CM*_XHaMdsIkKLoy9rAXJ6p61A-an3u|pZKSiqj z=|I=4KJ6AeZ7f;K4;25p*r$y~EJB{uhc%dqY9 zs;DF>F_P8W53@y&zd$6N-@Q9TYzwa@#8PogQ(Us`<9Dx^hi_<=5`fu^ zD?YQt$)l%99)+Ju<*yWC1>@tsYbY-4wBn2Yp zgEgyXkxO@W%?(LCCTb5;@sCVysb-s_Zle}|&x*Ajrp`5oytxI{y|4eGG=V84=Q4gu zaqF11&#BRH3~l*mfMRm?o?@ig<>~MbFXlQ@0hYu?al>DxhiMn-W9B*?g%PkvX?Nr3 zTkA$3q{rUJEe&H_w68KOiK4Ot?|`W;ae_aT5i%}cToPvA&nFDMm?*4Tm%<4ejM zV`{U!i^O8Jj%&}s+j;0t4c(h(SKeL$z>Mmdi%7Sz!C*I^2P<5vNVBP5q=bRHLd!t~ zeRMpuEOEt6>HL-7{$sI|(!J}Uy%Ot>3FPuM#=8j_U(GGbeVMIbha9c`K48ItWyk5u zZJk}3C1-QB`5#5!z!_HWV;`N>&>LH1pUBwEkB|cFsCD ziC**8y9BvaF9QPw>mMB|K@D77!*j5!HA`6fxk#pUgcS{Y}cnBveOq4nRf( z3&wEa^^vaZ*Z%5Cic=Vpw8h?B<{^ZdgIw6Ih~|bi_-LP9?T5%kgviCv&YTt#Gr(bJ z#FeNAn``wJH|iF*-l$DGeFQaa>ct&7iZ!Lfz>IfNt!OzPWM&(sQ4xCq#r2LBu6hP` zl3tv?_}!hcCl_h|gxJS&#*eB4oY>qjvrC6KVgAAh>p6zSZOMDMX(UA1bJ~a}QoB z@A3U!cW*;(4LY0|+gzwN2TTrh`@tAqjZxE@2cR zOpI28C*I459*TIMxUot#um;-ey6dp-sdNTvn*n5~UVF!l5JKM_C;x4eY}uD4==>!7 znb`rF1+8Y+`P=MR6KzgHE%J$3mDIaFB3$ilN%_D+7pKl^dpD41L$V z!z+fNlw@XWPR_M=Q5Uefo>ik5vhWb(p5}yoaIo=S7f)CDFunK=L4>9z_oX7E8K}|{ zCO#%&wqh<4^85GS2hhP{takGqB;!8jNwxmWa1L@gvM`}X6tzb34M;^##zNln#*pRL8~YBSuNN8BEaH5At#7yyNkR=fRm#{4jYas~#*!|tJt7Dt!OW=5sU@o8e8?Pc+%fOb=~%qQhl9l} zHv}U6CVCq;H7>MtJ$*6@zNtA6&)}$Qv|DSAU1%b_UGr-??_JgvyIB7rJBE`pI57T2 zP9ak6!pJAdH~*d~vm+81#E0LkOZJ|IvW51DX!$IPU|jH*ADKQ<839MFjSC1FYS-%q>T~ zoP@Mz)xsXp_|9I;DVctOYP1(X-dBI5YQs}XX6fy0hdsi-smtSi8=~*yI-Y@4 zKlO(~(_~tGy_n<+`}7J2prf^u5c|{T1v30MDMKVB;U!4cRGl?GC28h#&^teHtS71c z!@#dfzraDdIfyA|*@$<_b?Yrte0@(#BbCu>c2?pqAI0FQ#~?ry(#9;dY(L?j7#)nP zUi%(lBF6#gjF+_zjN~uDw`k3>%P?A0Ce3iK?Bib(pvea5h+1LVH!WFcu#S>C>Dc+5 zB0J<>T+A-ao#~H-4{~dM7y=abQ|Axz8j3fQXcllcVA}t1oY-;4_UCKGMeJeRFfps* zdMLLSUC`9Yg*~+gcE&`SK|JLI&Ux-WA#ga5=SLo^K8Rng~KcX=aV$7-mTaOsf;6i?FYZ=V3`yGZ^*^cZsG;z`u zg%-G}yGfU$>~$KBA&901{`b)Rx0wF74G^#gUiV1yqf=jzJ=+h}>iw$54dY+Ya(u(R z3?iHS-Yl^3t*)B#`S$R8Rz*(J7MeJe2%<8u2in3&>fv;bieE?g>mYF$)Hleq!eC?4 zxElO^yJ!2&&EFw@r}E|l92Q~AA+&KD&ag0fc9n=^b|wdh{HC9>ebWm3t%Wm*+G5(E z)24(ZE%k%Gq&QsGDC*Y8#Rad4)DJ~$Wt$ChX_U$6d~qj`X;6B_;Qf^~3Fi#s{UqmW z=H+I77%jotk0ys0jorp)S=Wk{%18+V4|yLEHensrmvgQb1a_XbXoN9m0w9gNfRP$~Qs!-rY5~*0 z%4#h-z>Wy;DIAfHHLT9)I~@Ke*{}BZ-ch{5@LkyVwPFvUzv#>}X#nQ(_2Zv3cAl0) z*x@X!sy$jcb#kkKV3SE}%D@Gvo^yeJ-W#%lu_CgpAL3O#iP|fpe{N<;uTFo%J_``s zu`-tPyf#!Zw#&1XK8BwpY)ekJDY7p^jgdx>LP_&SD|`$=LCF&{wYU0Wd`LAP*E({D zgBG=CiKpH4E+?tQ{W=Kt(zY72qC-STsY(J?{qMIyb07}|uJc-)*482Fg3e+|2k!Ja zakB+Bf(}L0!5Z*s6`wKaJ!x-^=LSN5jv>@Pj@1}#gyxz{AoCLyb`Tk(+jW&|gCB-* zSv+?^L%VeXIKTWPSd@MpoB}L1a8&%OzzXGT=wJbBc!MF`@hL6GTd0pnzNAZXwODSP>XN2w_M!&XA`>qv=n7U609W$*N*mCfr5ysuh*XI;(2t|wBU?3a`cxZ&0(mf&ul zF0Z!fnU3t9+D;JEhC9iGe^(}|$h-=BQSv+WjcTRyZEtPvS}tdf&hc-bNB_Y1h#QQ_ z`pD_T} z9CL8mQoi{&@kgvBEPu2k$0keLR}zoY>_*%KGv1>Hv)$M7>^3m8r7V2dCg+h29NjEh zfbnUlAx=;~M1OOFZimpQ7pG+FyQ;ZyZLI=%c8k9}fGeh=21i6FG@ring0HAibenwC zheqP*s*M$+9=2-xWDR)IHdor#l}YUtvs8N`E%tZDJFwt+{A=yamHr;8od$uD94cnh zzYU_AMFsMy6$A%1gx}@EMuQ`19mkWL3Qz9y(sg(W4JTEHAe!s$B zvt%CL)xo#W;HRVGR9PNIt0cjFcO{F?71TO|+iH z(_h?sytOT2xnY`R-h((GLMJFU?{#24HWizY-Z$=a-vk5nNFYs?vD-|!7 zzR$<$>q+(kexR|+lUWN}4{LM&VA`VpH0d}eAyYNMn(;9N(+3b7-eG5o4Kzd^C`)od zcW+@0Q&kc81oJe+Fu&EEJ0xz_Vq2h|KY#v<=DfRJg{v8-PKV?`Xcp`7U zf5I_Q=z@kAv(?D77oxq?-XFwBa>l6+Jgw4d;6Xjb zcf-ymVMdM;|3r_DwTzT;(fLoBhK|I#dpaPZH`+#X4Io@Cc)p28S9tE_g%ojf1PzPQX#BR6U~x zLWDF*QB*$Gfx0{+W1_c=y)|>QPK1Pw6(J2)!pgIfvs7O?gG2kyHEt2Pkjs;aYfffO zJqU_e`WDqW#hhqUgAOs|KFo-C4Zd~|#Zb}WW#^)KD7ba$H?)pjyWsyV4?y|{Wcssk zrL+aO=pO2mbqV4+QQ?HNI>Ic!Baw%}~N-c;2LoB!;s5r(<2`|azIJNT#YzZF1=>=eSBX9tKD zdzSyWDgEd+-6<-5CkEiWrTRA9lZmhz1`&7B8meiE*Wi1Li z^-M6M9*su5bN#n+yRF9(l0(M4Nw#+pIcUR{`jpp>M%*cNxr;%lR+Q>Zq8oTKv={!> zFL%5zT@uVy91b1`FZ(e<`B>A98~KG-8rpsrQis=IINn`g(PXeTybU(GHE*tQ6Bc+mKW!6n|As14xej zZwzL;bj26MY;8d;BlEFyiLS-=p?1E_ zNEb$1Fl6R4Zg6L9L4)iG&-h+9$DKPnr*t#rRR*N;`L>3X5M4e2L5+MQ+^{%2Mmgm@ zK^3oyTo#RpJ>m*tGDW^3SfgsEB)Z0$WQXMTw!nsb$T08%5~REl0sv4KW_z&S3O^uz zV)Q??Fo=~F01XkX|KEy0UceJJd#ezU37endkdU28sh>|vrc?MM`kVCDc+}1He_5Fm z1{Zg1>HGLzB;2Z~06;VlB76=J5g*`PD6vg8mrM!V5%_mDEI}jQ z+GTw#$CCHngaC0A<*}k5-UM@(MIN@4juaf!2m)EmE;MA_tW3`Jn0HlMy81&F;h#?~ zJ^1j0*-N;9`a#vAZ1khp!%`muDMLoyB(eTG?}V3l?xUJhnPxr{f`SdrN?Bn07e0+z zA;=D9U6ME%MosQx$cSJqx!i$`R`;DIL4R#W19_EbL!dYBni&dy5olTCpE}@8mF-lH z-CBjZ5gNEgwuWdTHgRwy(RNPB492!}+p%P)lEk6T-b#SCZ33w71H~5i>WsH~;J>R7{w|L8C0evJoJ#uJ0_&KdYJqD5$E{^3 z-=!gCm?7`&bo#jD;dX|thMkw)a=Xjn*oDjSV)SW8PDWOnJjEytD?qcd=oEp^;ni4k) zk`WTQj4!8UWM;?r)SlZ{v|qzqI1yO29XzxQ_Kjo>+SEb+nNhkO<#)oK;`m;h-5_wG zRHj_Fu`nUUn7pfE*m^y{a<9Vdi=+}@KvG*%*J_R@r17hV{3TKDwA1ua`Z4vpMTNJx z;2#TUtI~%*v80TQ6)Fg3dL7MQYQGZKvIzn(^oYN%wF+{tr^Od;cX3QPgbDtz#t2+) zT1BQVAHNi}>hX46cVbGOU?ehXw-`=6ZBS7ZqWNts5Lpir1-{JLgK?9m={8#`m4Ho6 zTJU?SMAPL~PcJ*Hfe4oBRSWFTlb4wTQmvO_+WBOFKru?>9uq+`6Jt-v((%8pH3@Q` zq{V+k;XBPWU+x?~XgFn0z)qXbiWdH%-1j*W%M?~1gzdW@#e?Z>w*r$1t@WW zh{HtqsY5Ej6!&SAhdFFML-&bIu|OL8Sjyd z#3JI*uxtAIIw9KV79`?qH6h5w!Oh7l_TGGdVImOv3+x&{QKiA_X$8E5%ONG;KC7{> zc)>^P>;0to0+~N6K!$bM?sk4&Q%48U%SwN;8tnyKdVMikJlrTH*!aV4S*u-gh1+g9 z+sU9q{=ND0UO##|f{CSNNk>OVUf#Y&$LgZD)#$7I5yV^XCr7;>7~z%wUhTf#iwfee zthC`rA->u%9ISUNJ(Rr?)S?N=`jf$o`Q4ufda}Mu%KvbJ-*F#Wu}|R9Bob09 z3Wpho>fU3hsyZ!n_IGVlg^t$2e?)r(L3EksTir!DI~%DuGN`Ju)zrffmevMb6gKhY>(X z-?E_3jKj#(u{cT66?xc2;K%@QHRO4a4N_i6QNtPv5!@Zcx1%1&Ax5g$+cqpM0M4jy ddHEGlB7~rUbCh-n@~3|~K}1AE{YXPakBI0h71a1-7upMO5h0$n9JB%NfUBe@bl8H(fxMa$;S_@Q!E;c0maL*^d}9pQm9 zJ$r9P&7bDP39)|J+t44Hy3gQ$+Uj&Yhz<(EqZnX6iGCeR!U8|j!iyw$I%vwGzY}+`rON!S*nnu>q zmK0sgrY=4^Fb!G8Eza-X?Snjn)x9`)FGUu%x_4YHYLWf)i|JRhwkSEl)b>R@&OHf? zb6<2t5q{HJYaW8NQ*RuEE?W$V7MWjh-{AUH5!Cg1#O|@K$9Pm{cQi?|%%~Oqkes8m``#bMuNBI;-Wiwp zu~*lIyURQN6J9|%W89cn+1{SR+CTd7v_X~y_{t0zb`}88uIyaS|to+EQ*Jg zhIpO`^Wefg>Ngqo#0@tp&5%D_yWY{@#{*N!HepN!vb=JG4nCv1^;~glQ3MT1W_)ae ze^!sn!V{OsI@UWH1y=QkQDdx!ZtP9e8Fib!B@PGjCH4F20yDCm1H@%IatOaF-8u5Z)&GzlGP?{xD@OgqRuPLps=o5QbPbB5@Unj}4^-AL1P4 zg$qVqbxbV0h8I6|x!_**RK2Or7Ps|1WC7z^eID7UrbtyLX}0=A@^?2j8CIk|sL?t@ z+}j%}aOk&DXsFo;=o_i1lx}EP2qyfkAYo5hxNkUyn6RnP$7WwRIkUkfd?z{i&WYVczem~WplY8jp*N5C zqEKNpxnKOzWWUry-G9*s(MmfxQwRyN~hS!dQ9_8DD z28&!LMi=rK6=y%hfxo|0VhMH`h#1vi(v}?le!8WHZ>r?Lgz7jIW)TQdj7aCm~5Ka7NAI=FB=)%lzQM;#Jt&M9T&DA%t&r0ypW zwf|U>WtoU0buZI72J8GC=Jq2NW_h_#WPUFy(AT0e-BX-m5L{+VtXq z*%4_(rDt*r`gWp**IN!q}o8#<-;?q36$FN=oo=7fYj71i3d3j|&NHazE4j7*dD?wi}Y7f)i7 zlV+lD@PSj{E;e4ea}JX%!LsJ^P1@1eugT>)iF{`m(D$-ShztrhPgTy(_iig^b*Gj3PEIaxr>A59V;#J}4 zQm=DE$bsnz`()it>hxkMVh)z2cKWM$R)WNweZ?%ci8OXvaLjS~p&QbZ%O0;)GSvVVZ@Fcv~o#PE8gt z0Xh4YRuoJx7UtMvQJ8?IP4m&!HiV%fUCKXPd!27uR7Wux&!?}CH2925A%d=K)!RNRUvVpS((C2m(u7h^k;b@ z>6)G2fGVEm$&LUSixn^?xty2Wy=?JHd}py0`m@ZA5l;Y`nYyH) zMEyQN^X{+>y@x+q;jtANxlOo8iCz$ygmF5EN2@9@2+VzbC{Ts5CFpOUp_(|qJ)zXk z=}dwujV2x3A=RGah{Xp{eC0FZ)y6<`|NkJ=5Pum>C)7`V<5mzQ4fu^N`mg&HivS@d z%YKw70OzfKMne%lqzuG&GYxop;nS678b%p82NOk>b7-p!$1>t@!lLY9k;Yx zT|;izEwlf^`WECN=k#-)l)zQv|a^vXoaiy+*>i0O9f=3H9GGPV%@X+~V@l_(P zql_3PM|-+(8vlIn-$pd#)KiheauaP9?GBtO#S^-1Q+vQ5K<=P4Q~qoB*(I!$EG}DH++LE=O3(reiIM}pr{O96lksZlSFf3c z0zNSk@e~+F3=P;0&a026lwg|U99pC^K!-e^Yi5LeMq!%o2n3)be!iU*XTm^9BVK=i zuOws-fz-H9&=hfs(utl}??P)?r2p;%LI<*^zu(rNZ@8QcyVEN@Ub%R){^-b*uo%EL zaF{KpEG#rJ_g<&t;T#16rv@m6onx{soW0oGo#0ERXyEl_J%`2Gbp0U4#jC@{z!}!L zd$5<1bE0Yqqv;ir7G$+asN$9Jz{%&Rrdz7E8g4p(F-m?y@(h&?9-*r}2~%D#j379> zP4K&)_jBrgOBfx!XdDxE?L}wv(Qc=EiXSjW>JTWE64A zKVQ;|e1S#N@j_V>L4sOFNzlMY`X4Q|Sx`b+U`PN7kOB0_C@Zsdh8aozZu~pLjJE@l zHnHe_Mqqox!Xj;}L_U~gQ87V`?gml1rCY9<3ZDdrmjDUiJ}>V8HTY~YD;A;^{nza54(hy;D?IQX5NQ=vTU*Cl>YQp$~SNN}pr+rb4 zQppO({+AE5kAG(p`txg_r^Di9j;@C=$4V8V$ptmGd2~G`_WOi(ZbsItc|{H#Hx`6paqQE5Vb86g zUh7;8VedFK5?}`0yq!59hHM>$ktATMj>RAa>-!f?&1F4UI|^#N9R+Q!7%7lDA?`7N zVe;powLP3qe{8dr7suUFR>fP>Lry+mptlT)vEaFT8rUj#{HM=peoo^AAxTn-xQPWG z#2DR6tR-7XAX?^{2eTC^g+v)_M@{>|`Su-3`*B;_fkVYT68Y`L9J?h&McqaS>Pc0? z3*-XnNo#x>#{a^fEu;3*ACVn!=`MVJlpL`#ej27O zPP4L~Y_ZIu2dND!r43I>A|A+H<$wmCzv;{L@A7Jtxj4>z%E{=h9JF|ao^JGXr}6T`u-_b4l_h0 zeHMlhdiVUERxwAU7B53fklr_@IT0(-r&SmKvLEFpk2 z*`SJUA2QaVI&0a)O+v|UmH;Jx?kWwc})qKB7~dYNgFw1 z6Ar^c;I3cFs$O1|pVRi_oUpy|dv(t-)<<`0!$zI8eC99L< z%e6a%#i+B-4IVwh_z-`^G}K9A4nF5{o9cXJU=KfWcoyDpW_vhrdFqG2D4r-PJn}z9 zG%EE7c_XIZ^KGTU;^iXu79$@JxNbpImZcXwZt+Ti)}=knJh8gp>|N18T8qvlF4Q66 zJ;s=3&nOUMB&UY+k6CrkBE6t+UwX(Wl2c2{^&>0_dQlp;tb(0ocX=2T%(8Ea=bA5n z^9rvcy|McR#0d53(0~i+H$h22Q^eM+INab0DdL9Di=`ZUy9X{DTv4V z%Ps+INUa2K;(R<8z@k}fsUJ+)f<*(FOQ{)Mmjk5)qnZL*TAIGcXOVT&$mIEibhj>A z&Pl#3k+#HEInM=rPD>t)&o7P^fNz&oy0Jz;VA%7u-Lnq!3-8@{ek{Q@BG+-lalHD1 zeaJ~pjqIgP?}awc3e22=WugyiIz6TyD_p1B=riwevKW>2m8`+|aZisqxG^d~7%3Gm z-`yXjaL7X_zx5rjtI{s(xBsZGBtoYiFLmB>sjd|Pn_Jx`=4*a~)##y2U`O~hinm((G?$Bh1-hu?ZLA2Izo#APp z%%O*ojOXN-W=V~sAaaSAXmb5f%MWu+`8^d{EZ&3S=x}&b^Bti8(uf^urtEVyC4EZ` zb$PQtqLx{}wjrqhZ_SUM59PMdX35%I(w9 zK=_-(*C$Pg%OuoUt|}q_7jESkQKuR826-Qu>CBBRu#`dC^b4;H_`9-1^?J?=17Mw52( zbOtbO9UsnV;;ANE*wiaww^s$Wf)f?_YLT4Sjf>4?wj7h`;pNLaQ-%#))KyO)^ zX6wkVu4aO#ouhS%MbMN3p+Gn!Kp&3!9^Mkye#c?{fSf7&9}uzO=3;tA%M{kcX;Za5 z>C|*saQ^#&9)N^xoFZT@Z7dKY0KkP%@Eb{CGVb|c?HnCxvLxXvP0hma;^|14(CLMF zd$3sDp7vVKvu#*kj2v7}%!i|%F#34h6#1TJP@;dXM&I4tG=i`lr|+(zwPoV=UZSBQ zWZI-SEUqg*d$GsytNO*_anK#Ho)x=df?5NaxRoq zQPFwnE>h=na_!uEY_ImNkHA4xnMKeQi#85r(vU#g3A_@0 z7akCPFd@>}{7PjofyH!G*JZ2gaf& z9&Fr^YUMK2JKrVqcZs6_@0v>Lj;CGFGs19J65l4$3XX90jzyeb6Gi!_L zIyNUX)$NNW&e&<)5C9wa`|Z|!-MU8&R&qy4-u*S=w3M~texiX=7)f7n8Mpn z|ZuF#q7k$pPT@z0gB1v6MO$!{xvQ{Cq7p?2?#M;TGNnipqxi=XMax7xq$d7LSLMBh zL&8%y;m*Bud+Rus&U4$OKPxlKfKPGI}5Y&&l;owi)V_>Q6Rnko zf@RvS3m7>S_qG6>^$e+!T`Y3rJOE7v)*RuX20wOcmrpHJoK*K{m$4hpSiGXFiVV3A zE2d1HfYPGyq)CQn5`rj8V!?A8@u$Ci(>mGAb#coM*@y9)nI4*}_@S|6yVikPJKXLy zNa7i1Jz?Hp+`TO1<pDOhdlP{X2n*gPHNVqG+epJ7t{f_>_C_=H;Y+X0EVPbQ)2K0=PMo>+JsU zQCVHCKI-uws`Ilw1D}R4w*`~hvvK{xhgMVAUuhRu#-0LLQ&ijs(a`OcYnQO@h6g;0 z?q^&%8w5)FyWi~Bjw=ykg3WDwC+~R=nRXkfP8V`G<%%h7QF_en%XTT=7CrF_M-jy) zAuGA+7Z$-3$4_St)E_Szdf_zLhNB{LXNZmcgOpp}f_Z87`yM~JFQoxwWr0KOyVpQP zvHQ$4fSzbcMc)Z)2@)+C=qB5@jE0y!XBGGc16DDcj;`Y5A|sDaBr6}_$=X*q6aQ*7 zP_*a-7n72jpw^uA#4nV|_V5dnnwQDm41?-h;W{S{em^=FW@GAAF5u&|OG zADB*Zb5LRBUU#`1wB!PG`My;(@7uJxaN%g28n$-`+L~ORV?RtglVyBKpb$*lQ7AAg zi!f-ktj6yXyW8#Aj~h;DT0+Q%+>_Z`qwYo{Uv2gQb(C&A%U+Um;g2= zS8T#@9MzrOS^=Y5LOPxMi}x6$<$0Zcz&XOt8#j>w6|Qzt14;mtL`ylgf0KPk>WD^XHi-9K>kn zsVA(MS>f8!#^h!ihTUsj=NDGZEO+}U8K(a&oLF;^v{cqJYckfS^-ax;LVBbT@ObEa zll2=wgxo@9X7TZ6W%6lxi<5>vPx`C4f;+#VDW8nAxjuTn!O>^S?3hWm6x$c^G#tZ~fxDRzSe{XXw?>70 z&e^+;)JWyU3qDQzRi_@*`-ew5Dk|sDn57>qDae#$evoK%35bg|(En9wkvWn_Gtky@ zARO4al~=q~P#oCmc{(9>oL{zpK((gGc+ER5#v6atbH8Xv>OAdeFV+mA^t9EF+MCp4 zVemg2I)Iz4nM$dq+l86Vh_1vnVmsOW!(#W*1_nk)2mLIf@Slw<;f*29HNUOwk@(m% zdHNFbi*ve*X8wkC4q;^IW3H{Qvr(TY7O9gCc+qF`{HGQaYnnyhglm1{w{qyL=2==m zstH^SZA2I?E{)=)w<0$fWVhb*3IkpbNR`v{lwhGxWPi!0i~*ntn+~8d)Bi1r3Xril zKPBe>Q#*H^qvedgjn61e+^)eNgD3EP%hR7E9andAls}*C=wDb+s;3VBLCEbxmV1z6 zi^A_4KFP;K?X1oVkxce!< z5Ew6RMoOdcY}yolQZRzw_er)}PPzspwW}K@6B`s8N_h;bSgZJ*Ff{Pw=e zN;W<3?#y*!>e+~{M)({<7MGU>h#-#NjpXFypM zZ0!pcw5W@ip7sqMW?jkhfPw|S&{MJ&NI9O@Nh2uwTF^UWt==+;8JUM}$wsM>I~%(w z)lrvU9LkkFCrzR#0wm!5y~`T>+r5rP_s)^E!OVSP8-wlQ*V&CF!ZzFL(Bu4yg1!(X z^<)08oKh=|)~oNeR;&Wlc~#P}Ls!uk^!?=!*mjP5klf>@cg_7QypvX(9_bpgT3PgW zjojEl6F<9U^8f_JXS6cMX$LTj2)9Rpz%D^z^>W-6E`~j|ydniEF_zgJSAnOjfeB%hw}`sXEUpZ2D!7`eRQo zQSqJg`MTxttMVZ?j*y@#=G%2`@y40gp+32RTWP&QUZV(C@Z}LN8O8oipTd)O3x}|k zcbqBgO)qY96ZY$pWG`MPkG@kMW1j=$-?}+YqvBA&rT(wLy$g7h6v+@!uwd4#0=@df z=~Fd}*J?%|b9b}PamF_Vbcq2~uWn?WSO|s7d>|Hz=Cl0v$FfA1H=s4?Jf+Ma;igbc zn7^IY#b^SH$PokQYC{CPvlPX;7*%}DD`wXyqfQ=KGp^rlGR;*$eo|8``E1NhAa|6ZE z?_S%FUwYe9PymqaFKnax^;@;-Z(*-A^G535x=k1dhk47*5)4`AJTjN5$Yb zk7;HRkjXm1qN$j=J5=NA1e&irYr!~TSR~dzHgVlTIWk_ko4CccNwpsQe7wEAJ!`2z zad7Tal<9|ts?Y6X8@``=q7JIQDGV`bv1&S%sjA)Eh$5Jz>$YR zXBXrckC`CURuG6mPBdCX29QI++yU3ZUk8(at^VE<8m0 zBcKhs1F!V|IWbTbd896=g0&0`q6F~hz^!r-Qu3neo8`_>)o=ja03andrl))p#^Hb3 z!pyGK`4BT0h(_|M&@#RJV;$QI78wM!U7;7HaSvT)aTQyTm+oN~-isd5ZZ9W0UwqbY z?w4-PyhnPxQq^eg*j}!U`txk=*>x-B>I>&e_m*hZjlPXwu6j-qLVs{3QfS7Ibvndb_26z#tMiofH zI@-7x7JUDu)#{2Heyo8CiXI8F1j7#r*j9PkpE*d<)XM9P^|GDc9pU^UD zYk%&1uko7ctCIlS)7|pc#gVUOf);mb)kt{oO^)%eXb*aml;w9_HZT?@JfC|Ud`!Cc zAXfv=UOJe9*@u}+8{7gE`#79_d`?>}4hWLe>Nw>oduT;LS7VP3qYDUn86zWuE`Jt(q;@`9>#bxN*u%sar zHmiWf8;nlB#!@bV1`2#MicG;Ky2o8H8E> zaD7;eN-~RIHpXSmOZ(3RF|Y^iKFi>|>HN|1yM$=;R}qYu*U^F%fX8eAGIZ3`+&_p( zYJONDwxI~HRb`kqht;0%{EpH9TpV1H_sX5LlH9<2Br*5EXI?QFhnXcTO zM&C_h{ibN=sb;iQ?Dz+mCwT-H2CgIY&q3%Q-(qrnRot~cZjFg~R;SwMJ__aS-Lx;vm%TY(1P&rM+w>i0>XSN4fTGDO>83r8 zeTVJL?DF4v${#kA8&cPAdWvf`tyS6+ zRvnhFt4D!oz5(?C5YWa&MD%eROA->sKjJuGL(eS*GrL`{S|7-%Rm>^%o)7Nb_(UJz zr~-8V-G_L_J-3-8UyVl!_FNoZd}dDn?;a0PZV~F-g%RvP_5~`jTPibOlcD@DcKF}> z|A?Qcd^G)FE{NQ;VY0~z8uo8GL|m%_%x`j)EjD2m_1aA8j(V3L#i9RAl?XA&BLh!| zVXO(Of;CdFNyEQ|gg=na-1UR}Hx4`3iapNrmygQ(zOEe%h_j|_lTgGFx4s22zR~CY zXA&YiT+;yKDUJ-^6VBA9-cbGV0)8~-Z~V~TqVA(-s@?aiXdd{sy4ut94j*i9pz18F zuk`I^SSzn*&()VDwu>a?h%Wm(>o&K(zsvsRFs)4KdXn*XG7li|H^_rpLOIVgrbzBW z;=HKcpjR2};f_Bk2!b`e3lR2s{|XCJlY!s7s@eswvxCFyEcD~obN==DhAA@0_4}Ygn*(tq+l~|cr6W#A31|F9YL;;qAVy?Evql{Gt zaFVr9QnGL*S(w&aSRa&fvqZ-J>yUB#vTRD_EAz(v>n>3AmPWvB`{LL;k0(*zOhLGoR}1EF!4etAG*=<^AVp zBroMMWMz|o8 zva7s5<}`mL1#$Ud^2RgL@Sm_9Y5KdPJqpc2Ij4v`%sUr=;22dw(Pv4UV#?@MLTp}? zyNpqk4%_PO=uod3zLA0ZlbJs{-+r?G*@7Q5?paZNW8sTwc1k11C=YnV@BpKzL#y*n zf6;$<&eC3Ri)}KC))9fBb^H*TqwIdV#6Hz54i3M(zd(%( znNDx?U-3Bk6)|K?4Nf^9$*n(>F{gVW2!DH5ii@x6J_8XEHw99jm@tluB{av_L<)B& z@(BBx)lRBc7M4>A2hDeT`9F@ZMGlo0owAwnD!1n3G;SVw(KhHPqO7=5Uf0h;{ML`E z3Cd^|b}6|pTMcTqwS|3&I+f38!0#0GC`iV6Be;cJh=?AdzP}m~GO72CbryeGf39$z zeMVGYwc=xPHrVr3_6Gms>U_kk%}$jRp2Rp^@l}Ge`SPPZRLQ*@{QXKFiqv< zeEaR+zBS&yg4AL~85c2Iu&pr2s?5o?a+YuD!}Y?jfk<1t_mrGW8Q!qWZBch+k?b<_ zJKMqXfWMG(>x}%-?Q_A?h>;cjX10qLT;k;t#*3*wpHcc1AL0xhGO6!K^qy`xrB}|N zl4bnVuR~Rfiv4Dhd5(0t>slZd2_QByu5Eu_ECLJ#k5&NZai8X0~*>NGE zt8%#Sd(TGJKDf2aYf8Dyc0VdBo{XmVgQhHg{Kt)&etqT4lsk4+4%Fr{dqU=~bF<#X z6zR|G7-E+-Zh5eHN6V}-^`XssdXF9Uu;mB$WlB&oEGEawBZpRWO^Cc;h`cJ6)fOX&N<&-blsLgo|L^o zU7V@c-V1g(L^cBPQHdC)V;;{7=_z5k?ddFMKXVPx$8!Qzsq<8lK=EUmA>; z%=;03x6FJU?-JbYS2s)p5#^8cM+XmdfBhg4N30r*f!fo6P0}C!eH78;7g?|9rQc)E z(cyji?gjpCI_H19misP`k9k%Y4jZ9qM~RE_&Gr!zy{&x;w>Q6KcNJaoclpgCa%(Ek zCqh&1YWK#CPha4Ql}XnCRj>!ls+&8f8(nWbiIu|Zn?zI{d z8qbHQPFB1uvKr6u{Z)!Uxwk;}A(ODg!0fSjS;>4d;iqnc>XyUnjL3_%xM~oXQ~kfZ zdqvvM^DXLl^wa=*Q~J?}@33uGl(x@%)E569zC}NcK|%O@OOZFT)?d%+_!$m57uQBP zIAW|@FX%K-!<5tejZN#T_#Mt05R;niXrSkt=%LHfYsId!MPFICSbuq;xN(eA$Jv6D zg8Joo$Rc5f?f7(Gnjb%3&M)TeN$Mfdpu^9E`oMb8Eor}h5@SpW7G#DTaQ#CB&$9Qm z6y?uW{5-LC>910@?v`)-d@NJ!M$Iz1c#!T|IiXxT@;G1809gG@22w>MXv}9tcU;=V zs8@Cf4fD9JUqyqmY2Hl6%7+|lOnaNvd~BCSlvdv|iN1Wh4m z^q(>8olSzm$ux(7>cNqexkC}bW9GmMA2BndrkLm!it6l^S5ah`R@^EkEbj@jdGCf2 zm~@R5j>;C@4-&Zuy${s!7=&rJWm@^0qp@dklmcvh0>)T5RU|vE>ya8y&?xN zD>BNUzismD)FvjUE6WdtM=|p^%^mz&7KaaU_5&x9Ulo(fgX8LElnPqdRK~xqjbs1h z7L++6#T`b=9A0CfE9BrXHa^vVzBXU>WivBP2VsK_$7H(ipYca_?F;w$ZPoKM3al;e zK0cXjbi|}MjNr>ws@hxh;}y~w0)NR5dqp%n)zgi@O{eGOK};G^@SWm1J)UZd$WwP9bUJ%jnMQ6)YOINP^(^7+{NhAr?R6186JYrCAB&g4HIl1^0;y!W~!b88%5G^E?@fjC`$8DbokJ0Y5|P<0D=gyp>?ic4y|G4GC^8_RFp2fe`$R|C z_YQZTy`S((seC@saj-*Kdg2cvBC5tb0!oT!1P6Y&nT(2|u43A(@bb5G#8a_>NA#d; zMf#70o!{PMoKtcq?3-7+IE`rMVK=iv(xheDqCRI+fya(^`JHTA2RM1PH20s)8dQI7 zZ41IDh;FPmkE)55BHxdVqAGgGfs~dS(k|+|$*D z-7C~)5BwaDB*Y0*8S*KT_J-6)h`0;v zvaE@f+`Ton{;{<0*rt}a>)`&2XQyKdK>q&dcUu*Ev_#7f*xLJTVXAMZ&94Oi8T}!;@AAKhu;a`z`^-hOnGj8t=Y7|PGF}8Oo)~bd|NY51`w3C+-m%!BR z*Jm$#k*^in0Z1#Iu>+?)-#eTxQ*K4o__`K8TPw`T-R2)6SS;AcNV+GVsD#2&Z&%%u z*}GGAyuR;Je(+m--2FsU8~MCQ{Bg7r*2pJa^Cbhk!p&iLb+bwfwKIkmL8{l1ZUF1`Hofg--F-2d`YIqlg4R$5@4BN?=(;Do6~Pwc3= zp|0HAQEz5Ze-D#*|0bp67m+^klPg1&GwG`XBV4oM}?xoKju>2k4$7}SE zhJ&IaQ_Q@zytzZdJ%=fU-0E9}=?>smJ+I zw0)A2g*3=owog0OFlyW46&hqfn`=W?E+S9I3Eql({Nelou*y0U8J+niVg zAMSki9_eDq@GY}FiHlm0Z@Zf*|96OWJ?xm~w%dZmhs%CCpIFEi+yt&W$owd&jszgR zYsXH^6rr(UKZyl$&_DDnN`noOo6dHALgFCrmQtgxm>xMtYo|Q$)MVl&wV~wu@E;E} zbPX!{1NUe{wgaud+^WM^|CHBjYGH!Fo&yA~Z2FebK-iX?_F66;j?=4;(XLPcj7TEa zpDqJEuU>y!?tr6lyRFU8rmOU6mnL<=*<;=J2VKV#+Vi9H)b(6;KRItSc6vWq($)RwIS{QEE)O-^HPX`IZSCkin+Yp`~O>T`PXRW%Q z#gV_Ca9fzXndpiL$eLIvl|vwAR{z)--(si2nN4UISXi+Pl-UoS4Np-ORk;bKgc5qKVNmZUw#3+)9h=-W*nVa1IY@QZnQs0F?2ve(JTVjp`^kfNjT9*j>0Fmh=^_?A|*Wg70=wzED%q< zp2E}IZO#0ZuU+SZqi*`%fq66^)S5d3GYKb~D3glJWQ(!kM~Krgo$<2oG2I^Td&fL+ z57yE2FJoy2@DfE_?kCN>62uGIV--cTMBH&{N9sj1T& z#g?D*U|CpiO&T|_J{B>UA?FsI6l+~`yv|y)WDwyW+Wct((3ORx++n zgt_{Mb;I@JnkeA3?-fJt$l#qWbG3rQ-%(;J0*f&6nwpb#Az!QG> z#c@wx`vD(cmj&aK@pD=H#(qxjqPwuT>;cS8lQu<9;;^HLL(!u~cT~7V6<4k4TGm`c zHD7<$l*o~K#|#tDSQfyPC;a_?nvGP*ZJ8CPhLch%a8G?{+65kiEL-Z}Wm&7z!Ff`v zHka10(%&6lk=#oAx;*{rt1-A8pRYKsrmf8q$)j7kF*s`H^J!V!<5v+!WdIrkwk6Bm zZ^k}=&QXK;48Ti;})P*pN8o_?dS_}`#646xSm&? zui<1?!S~srh=Wu0W6cWl+iaCI{kY^)e5!-LU;M~uO0#inRJo^dMztr5y~GX~0=mv**OcT&cty{hYV4Dgdfm#p0mR@o z^Aaf3^2+awE1lpU)Z*O^hkBdj+rNe!q^7r=0=U60j*Ib)aBJ>Bz5B`8y<-+I{>^bL z1=zRPIV4=+G?ha3e}df&xyM%MaGH~C#cvoNC$HXMF0PCgHc%hxgu1pw_vGxfz8P@6 zqkgp<&~=tF%MDUsO?>F@cJ~f!eaYsTam+q`%45HoObtJ2yHC$e)Ay`sJqHoDg}K}* z&?Zdc{8oMxX1ZJU+^6T&>C|NVz%#xA*Q`9#z>h`Zo}H*^%for)G4m7!3Zq=*3Y`61 z&{p2RsP(PvZ7{E6n7?PZocz zoZ?<*>t4E*Q!$1du*PRH@IgUv~5XP>^j3`(m^OtTmW>1iVPZug`6wc=;z;QQo zfv|DTX~6^#w|4Z}ytKz>oU%@;AltiJP9*MiW#_`kk_ML0H&yU_Z zzTZD!d$RKSw3vz^@!}xlRaUbkJd~Y7el+4Qe4dRAAqFJZc=e*b*;x=Z!91fORcUo- zqk(s0XLJf&nLkka-R(!|v$ryzH;4xdedeH(5_88A6MMw&n{OHy#@t+bczX`?^EVRR zh}{pG@5#(aKW6eR>?ft?9)&5R9iUhvH*DH4^EyLRT-Dj%2kNMjQh4AOmI|sQN0VKO zX6m#@oF=nhB^egA*Sl^=n7=5R&hE>vwEGjjxNU>unETpS706Y>)(HBlRZc+9)7Z zJ56}ugUjMs>XhWyTki}yOo%%N%8XsC4Pw@7bv9myV&+p$baE-cy5tSi~-bC>v?pz6%QP1Id_~&!4EfT%1AT#M5Vx#0Dt`{j;5%1`uv(h);y#@R_@^|aos~RwSV_f2l`5=z&o03Ah3%x(>9s(pbc#r zj?K{gopsk7>c0Un%00$hRn5lCj|Z{l0;jl+c0+FPjvCyj{ktLlNC{AQ{4Sc}J8(`0 zd8|wV0&@$H{Zn!$?@LMAa>8!{E&6B*kH8mi0iyaJmEUBBQiDCy69DK$djpWYC>X!h zuKGf$8J4aFO6sqR z=7-OwQb_5zhJZx>jx|9LAUIn52Nh)GKxe*Xa=W)K`ofXCxdbHI;;u9Ivu|({n!vvg z!;nP;`9TJ4qC}vr1*Vnh*)OTIaOtKBy6g{NBiw~&{;ytoar};b2SqdIN{wz+>3RFU zk5_~TO32Akq4PL?_>gm_<%m=bmPL09o_>5&H!$mFCsQfXsEU32q?a7*r;;%GiqHQv z)ASVYCaq@`LLQeEwVz7ip6W;s>5u8rzuEDy2Pv48yvuIg#E=ReWM$Y~nOgXL$QMIixDi5}raVynw9t23v6rlp z?CaSi{F<8XP+Y>eImpdM4(=|~1Pu-s(#7@?tKP&kmuXcm;A_oy>v$ASYa4t=dU)zF z&2TlJKPl?#jvU?5UaS&vDcD_~$ExeKckr9)hW+iVrs`~|=Gu*W1fi4O03^Xwm^mJI zb9uLYn0BB#KH$9p2|1N92yD#$w0*C;=TaW&yumzmW7kdp)=hbOh$Ukgl=N@b<7EAd z9LXqbZepyRoqJbm6)%))@eyuw&gE3Jl7+7`Hm4dfwhhO$-EOH)S9KHrUqF`cjA zY|#av_1inXar-EyVh^v@qlb_EF06OimxFJvFm`{r(X&{plBP8pHvtwKlIB?l60(wh zH6!a6Wj408nr9z-^dT1mwrtvY0qXoh4(G%#O}v-U6oAbP&evxKHs*-k{Z`nYo)c#7 z;IazVoJ7>v(%gE@x_L*2CWr2SN43wM%ZB$2f~Q5R=`mN;lx{&}j#4S;d70%gP@pFE z0obk@M3-PL3vJZ5Z{E~!ZvRq?H#yYo*VLxWJ?(fSQ@L+-8#wG2Oh!ZBK8@0{xPbNe zG}xO)o@%P*NXF)s;rk&D$5OcEfS#}!A0OAHBKz))dKKjfV%(yS{8q)J!7sH^_eSyh zM#2K)sW;z-E^oZ{R6CuDg09lh3gzKfxcSvNSoF#DP=sag@semT-gv|xp=v9g zysC}kOqYZwbjoe-Er*>9lg8naD;6p~g{!uR)lNIfFl9ID{+(w*yMj8c)0;s#MllETj z+}~&}LT=6Ub?wSH43zj1om&6ndiKRB{qnffAv=!qFV5us@Me`tHp zfTq@MZPbP$C>8`!DGDkWI)X}Q3L+>iNR-|Lq=WPh7K#Fbi1a2P5PI)TL5hSbO}dm2 zAoNhA-*--ME%*M`KIhzX@3;T#B}wL-ZyVzo&v?d|?pse%I`C^qdYkstA?~~Qxx>)f zGoz=?l2QhoCjo4oy}&1qvA1)}q-6Oa&EfV8H%A~yWvwVOqOCJ!=evS(c?2<1^t6mi zScXK?8l1@Og$x5*ml2+xteT-=D#aANDgQ2BQz1?c>bdD+`48B1&+FO(88fLlX5d7Y zMs9V_l}mRl+|w#9GZZjucYb@PC1}ogC7MZtQ<#o5gZG0xp%zCxbBN-2vaWsJOED6W zt99+M0+%P3NCeMoFRywo^B=o~|7?DtH_!M(Rq#!-mYYX{@BX57Y)M@Vn%kVS9_<*d zVy4wxYp%2jE;Sp8+-6WCoPRWYN&DQ6=8;r$h8Q7%+jQ4W#By_#g3dOSP;2V1m@L#* zYm6H)zo&Z@pR_HN?l^5CRh})c$GSWdzFOzFQ=8UJt;u1dAyvlConLx#`~fTa*f(IB z$;B|Qk9gayT4885Bx{0L6*2kwd|y^UDV*x~;=mDMYRUGM5wk=I@-&gEZ&XGaBr;t| zH$@(hWFK&}&Q(c_c1lWLCZiuP;mj?di@A}`loo2j(vD~$i3!xj;|K~Nr=witk;|o2 z)g48EG1cQ{g4~8;uCQ_h<8~3S+ZPDT(Y4YD;eovRZo>wvOzew1lnBJqa&JC z%elB1(fjJio|X{l{lrfG{#%tqIj7)r1(Y$FH*ZwMS=yb8g=}i>>Ne3bZk(B} zadS#5lglCkW{*e8?Mh`j2aRp7)JJwHvm#%AOeoCn|M%#$~upe;bV6e$- z)89`1UX)%m*d3~?+-eoHQq0BhYExvSR>^FLJthfl61wTtG2d~P*8f5@ktsyyr59N` z6QxR2VPU)CO=T6mk@&-o{P#+W7ojA5y(Ec_<`k+gBt%3lM1E6(=ZrH&Ii4?Q>aeT> zrT&~#vrME>g&`eQhEb4an^WDU3N>`stK!==hL3)09aoZ#>4fPYHJAG7ivYLPD0M@# z=qm+Pv+-(hXY<=(8xmvx5k~Z97K|5}kWyZ#s0Y~@9<=T=xf&ccBsn?x+Lr`{o$KlX zi&<1GeuBYhDi-NJ3*{A3p@->-sgtJ`B%HgMI!7GNxu|b`aVu}sc)a!cSNt|I$PgE) zr>A-LZMFJ}t-a8)phj13Vt7(R*mx<<>I?pIkHfXikLb=Yt=Wsm5=6aINLFS<{Lqy=m%?6`Q+H)5=_|g^A z3zyN(OT^Skoz;w3eAlX~*#xD3)p5Zle?ilxogd|?Z~G)W`nlFPh(e0dITe{b&VCIQ z8A0tW#i^9uSo~4l!@Hv^3&_1c@OiEB0+z;RYskor>7kfQd&AIjJ=6XvrJQvW4;AGV zj)g+!5CmQwaC6f%v;)h%2-R`2HlBC&C>_%sf@a~p5v6Vi()F8Ht+xh#0O<*Y#cEEL z7ZXI^quQ)`_#T|PhcPfQ;VrT5jXCr^&rEpgI6zy|UOU?A7i9=fyFML|YEWOAzt^7A2%SRITl6zXL?eZNCWwxt2BFUx zR-R*2@$vB;t*!e!oH>3)j?FkIZ_1t7m=Mbw%wr>8bT|+On}t^?W+S}pufC~C@0{6S z5rRIndZhXUM(qozwplW2cJ*k~Li4sgM(g@`-6}W&JT>uS6?yqUvumZ2U=-oO;&(!_!M>T484zGRPVn2A=m24~+DyHmLzH`0^E{ zP2%H@WpHKyhw2dz`qOH_!%hp)(BzBPrSWG(No5eyTP63KNtpY*4EX&~fDyKde`=fw zWI)oAHN|xl7yG?zpryJ-5`tsM_l`haP7U?%jIqWheepEx;xE$Qe`UxyN2h*d!Q(+<9e?ez zDs`(+v*V}@JrTDfwR)ciiCUz6|fvYCKmtg?V3=#L9ftYZHK~R+qCwt z{b~Vb2V>GHUKC#7MO_i_G@;W>wD)IiA3Zi|Gd?Bgxz!6OeVQ;|G{*n^;?@}x9|BXt zsNh4O&!R7pXhiv2)Rumcv2*J|R#+EAq?u&2XrjvMf+j6I7m!vq^wq92`}UpmmA;4Q zkhuRSOY%GdvHFzggeZIal$;&KbjI?lD8u0D2T(z#&z~8xne)2r(-TgAO_l7z;?EV4 z7p+Oo@gK?<=al^ZIbvl_ScAANL%8C^Ue@%B2d|VrYMnXCum4TM-}r*jp?Vd!-aMyi zhvXKk2Au_g?$fJ;+~&aY!NXfW@|S(<(gRIgoWgSXU3%w=FZMWCOBQHm^TrB4b7&Wg zF)f-B*|$&k-qqNVw^1{)7_FvQ(C>u*I90z=>C2acN)hMPFPLd9Pkx%%$SX|lvQdAS z9h@vKXnFd&E&XE&vOXJzq|V{VOgXpNRY$?TGj&?!^u~0G62Xf< z-qZOE6?ZMr`l!ftl6N~s=QLXo?7LQlM0|y#rXAinY6o4PQ>-%=@zuYuZj8q_4}m?S zdoK~9L!wdKQ)(;qHkf&h#dpUG+Qkg@^bV%mNLu!GWE5D z5cLr`hxTomx~Ro``vT)1B9-I?vZ|lEb>^Q(&Ir~cz9H=lELmEKDqeq4kUa5;4Oemq?sk<0oMb+qZZRda2m*--b05JmNN9W~-% z+2OZZ_#VcM*edIOm3nrnh|?Ky2A;A2yZ71h}OIhWkp z*emHF>~2ike7tx6S8k8v-35BDGP%(Y9M$(DYdd z9j!Ao)wEH%qv@Z|wp=mti$V3Bj`n6jR7#|9Q%m66DXwUqqjV*7nd2o zxz?c>&YAZ9S_LGQ4pSg1+u4hhGTbx3+x}yH7w&}Fv*p^*p|)`3ksbpqb${RIUTl$e zP}B=MU9q|;!7je)NUWiQyWgjQwm68Kzfa z9<9Zjv4CdcdwA-^850Hl0^x^{EqlIj)fW}}QoIUxLTHctri_pl^(fnnH7wc;*z*4X z5+FT8ikY;yF@-*J_7R%_GDyazfcxf29w8g%Jw{AOHA z{l59Q`rh|b+;+R%cW-m2{X4qAgW6S;U2fSeC8TrewKMy|TDBZpp6hsjGN*hR>qVA3 zq8^3n9?#Mk(PQtJ{9Pmud;g55x{qY;`I%ZPe7`Q?9i3Mf|LaK)!__nUR{~GnDTtZT znSZr31E@oohepmZz$!1QsMw3P{$@*-Q{A3+aY}^G$kr2mNi>`p>1h)DG1UaKd+D=7o+R%lrn5r|K@Z zHdixKb94kx;(j@dSlsHHugQ$Gb8VeD`pdr4*m{e>u^Vp+7VA1C5={$mgNK5lQLH8l z%994+ygGSJmG9A}kW2zXe=X{gsK$F z6FBOcao)9D=ZPTJS>S~|>h-E|+`-O*ny9SErN+W}Ta{?TY`@MgeRc6j_QBv#TcjJm zDe~Uj3J5`43iP}tG=h;?_6=)M2hW!IX=1VplDe|10xNuDE*yw7zHtBNs1zx`bdJsg z&q*b=G@Vtm_Dtay&KD&yKX0PeCMv#*BG?A-g5eEHj~`}F%uB86RW!QA4CZ_*5U>!( zaD9$_IZXL=h@z{%pDjsYLDk>IkACnMCbKN^)z^SEhPc_cZntGdxcMEKV}(NKCY6ws z7dE1jJCi4(Jnd&4R;Ok^3T;@FSLX*^Sf-atF%*Bd_C{~y(&wxSIs>+?f-mi!RzF2K zs}1^o5`U^l3{@;gN{F4V_S0x)ASRrsEp?rrxpMfPhhYRM)tdA>R9kmgm0(6AdKAp&1z*j++a0lOr`gE3|I6ct5=#ZX*M06iQqE!tUnErXSB>+aZFq^-{!o zM?rsIUe;jox^?f-;d{eE(dc&Xi^OuhCA)!Wy^PUOOV_?Ejie#|te-Xjz20RxxgjgG zsKcmyZi{c>)6k9t?b^=d1s0mQZTs`Fj4}7A5E*L4qUq`BYx)Ch;k<^_o!=&r;e3Xt zn^F|8tOnk{N+TX>%XBw|U+(437wqB5RC}yStrdf~^)(UwgXlq}(BDp}lp)tcMg<`D z-FHP=pz>FU7U8IN&+yaB%o8MZ$v;*;Cml%TctPuMtLDnd?ySP(Y{m9(Ncsy~&2FVk zwn(|2YqFGctxbKe?eY?JNEaytJM$Rpon2@7ZIjxGX_+x1%CbYA`XbkwU*!AAVcxZj zhES$m3@*z2dg@5Eh5h(9(<{D_U$GOHF6n8_m;0WU@OyIk&}DaT=c|$!1H)4_cxf5UZI&qyO!Lht9VhSRcs49mY3%uj5@zMpL`_5Y!ZD8wqab9a|e zcA9vS8Gm|tP&`6s(4CCF_C7w^yJ=|W7GLE$@q;Bl#c3z;j}sbO0*h^j>>kcWjGq?x zXzx{x&2wktwOdj-Cx;K@x;$HAa&iu>%kSdSqnv7KRI#e0Aj7(;;iWO?Xf~j081#mr zz^?SM{d=6+i!Wt69U&x}q9k&)Eg?H^!&esSb+Y}XCauTb-~zpFZ1G(sKc>PDN+JY_ zlm){s0~d!#Pa}T4Rd7|76;~1RQ@x8TE2|}=;LBKytIoWi-592?68t14dZwd(-$Na9 zW?}#CYY6|A2m_Eb`-uveO%k~wy)9;BKSl0Wcf4gS0Lz^We9Bn4~L3Srj4a zdk?TD0?DGkd5TIl);Og*M<11rD=OT7F_DG+-aW*@yj@=H0j@xW~rm&Uy7Ml zpQXFqVD&kQwURYkg7kpe0~(Qz>?yxXG`He8!D(x}dyGm?oV!%Vwk2eLET%Dh{G?X* z&dc|w?Ifa1!afMtYS72TIL5ruzRfXuF>c0R;zq-mr90WZxt73lx6o-nx(PgFu_MiQ zj=QABE(rwiw!E+^#>)!pUyK3Mc>cnLOUG)!dK2z7{Yuz%>R>)d{2OlY^4|7e2q2MC zI2X#&%?!Ew(2s|X!eKDinNtWJceknhbeS@)YV|Cxjm2X8&cS`p!_a?4xY{h1F5YO2 z!7Tj_XC)b#n}3Z5`PnhPPZ2lGzgWC$5!zvx-yzv|F#t4VSznsu2_-TQP%sD|`x9t@ zVs;s@Hpb?zpads9-FmCnro?s2^BsfsEEQg3^Y066Z?5HOeL1k^BDhIGStUK@_;vXW zfvTHR}J*0n%{e+U243W zA)yw79#}jWTXkf%-t0BS%v5LUiw`fJ%z5GB4U5}4ccw}Xu5regafOg~9(G*k%om8+ zSu`{{NmSfP)!uP--YMx7YH2Hva#J@&=&)+yHNUqX_ta~)TwTEEZMr>2?T6;)K$Xd4 zost+xP%YHnJwXMs-5;@v8HXNjjeDI>=L)hYUhm6^cbO^*w~LHE9krw>Y}2pKA?+_B z?sg41Yo2M_;^dvQW?xa>`^dGkP)N^4(Pz0M8{55Ts)A!@qRJ%nxm{aHt$ZxwAT5k_ z-@B_xzS}go6) z$KfKiTD?dtgQ|!$USs`TBz}(DanYzQlf6m=5&8V*x1fNuzeaYqMu^`{ev18$B?e~G4uy4@NEqT zoLZNQap6r?^Tho|60Y@O`b#W!KZoM%P6yK^tl1685d9a4K}+myr&=_>>7`2@v=edQ z)p%?srtXUB%jtGlR9$g4r5kcVI1F*GcRg@&O!!e-_}H=GSAAc_#(iXDmT%!V(C<>J zf@9$8sLhHwcIOCy(xD*dFVAEg%a{Ts3*!n4Z{jjPSW47(FReM9{^4KoO!GnKm6H@y zf|jveGV}9-ZAV+;tB+zS+6CLPhgK;>+`MK-{T>I6+T1-NIF|*I2IND<{sL_u+f5uQAl{Nit;-fg;>2*w{sm<5LVq9N;TP1rmb~)W{#c#FTR7oOYWgzKl z$f+-*?+CZ1*P1K(`+n-=tkA{ihi&F7IpT@P72`ZCh--eH5G8qizK5KuT#=FPU!8R1 zEwFGQoC{|;vuV#i{D#}j1f|$e3f;CM)8Wl;xW4n57U2s37r(l=f{q6-{<%s<`Y0la zpe_-!(vu{`!PKDmX69^j?7>nP}zWwxn%?#{?)_jXvX z0%>)J|Foy~mP-Jj6SP5+;^zgDjrDbdzJoDjX(fj&$FvEjNi)s!a+IudYS5F7sh^&- zEl>w{MI;fV#NVNC<$6JamG2%9ToQ4@I%u`zVDcJUsrj8vQ-AN`}D-!FmqM_#CkTB=u=K4H# z+b*Q*o*%{Y&KUB=V68$?i%UP$WOqPIztK|Zf4SNTWuky|xz2Mg%D=-tptz_J*l=Px zi+E`{-gwU8SY#toK%nl+&1sajimiSZGubl(?-btpQzEnn0?vUPe5Bs)k*e~Y6 zGX(Pb^$xM=GW1@PV^!1GzrXtq6$^ICb(=`B8Y)M{Lq~;_#uPAGMb}4T2F8c&LY>z7 znNa(F%DqEz-?~Mr&EjW_xupZ=B*v6Mc>J7YHzuNO%r))oPs;ym zZFPZSo~`aD!V>Z>E>DYv-)dqeifKfqJ%nd4vlkRFbB=`rbZJbt*y)4P2!nv6an>w~5%P?+x97klb5dcF>y zJNZWd(5vZK$Qh;gt!p7%wntiKF8@~ylX;I?h38TeXp8IO6oFL$C4y3P<}))14~A6% zb1D-Bkpm~GLGZe6+dHi!moPF=xLKgK-K9BKINe}=pulq(M&=4Gxn?ouiaEL^P5JVY_DiV>(9mWZtJ-ThCF)uF$%8<{JeB8%TZ7u_FyZ ztR20*;X;I`ZJ+MvVq|5z+tTg_8a$3t(YT{3jQ*& zWcr{H1<0G&?etpLoC)VmZH{KhjY+k9(x-rkv|QMlnCc_WN_FS5edf2+h(R6>_7{us zd#)CfLxu43LCHUMs?{6ykeUqufd)G3)*FaJCVhJ`HZCFIig8yn+sBt-`5BWx#w?9X zY_C$CY5b((q%0-HFXKlA*s?R&NQZCZu@TyErF3t zEw)Cw`;xN@DP!=iQKR~oMS&*=K4afS!G|8+*bq(y@IPBN$|%RbG3q`DqfVpJ@iJ)j zswkcPLFgovL@aJ|{H%N!(EwpQ6aVp3Z*&*l3A-ju$jmOed*~RH$4jh`{b==JT8V9K zQz=tSb9PnG$<|D*LG7IzH#htRH6& zkHjq7vfVGW0%_X@qajN-+q$N@%h=>C?8T4ioOmaJpStaj_q`)fpOKe;5pF*9wKKaG zpCl~trq4pd-`3S$mx=qgX&pUndyfN+L} zPqr;S4)jb%^*8C9)fD(?GSp%iPb1hOkPCbRy9c5sTc_^gv6^}hGG8Ely>0&qh@+U8 ztd}O_NyP#Pl#swv8U;-1WP6f=s%pfhBd*%(gji^Rnt##XWR+DnX4A;*FVbssjcEAg zB}vENAjgolXy}{2zbh+7;<^EfO{$obF^tiW+K5iY9*GY>V`LXt833S7Df)}@S`vqo~L?i1JImrjFxZItMBXJPXjthS* z0Y4A7!OratQdTrvQ~yqEvsXmHiA+0meUE-iL1jN177gdt@mz2^a#331FO&=T#l;T* zeh&ilY|zFyH9MQAT4;9XkAWY7=4R{~>=&Mxn{y5nS-kS}^19S030)*-xew?Y_wD0q zzD!OfsM|EhB|~?}T?b5QH*1Eb>t6%y-&LGlJSLtY2Y>(0(bI^s8HBqI6_g^n%JrKF zdkvXG%20IdOY@8iG&HY2JyOs)6QQD~MqTKgSW9Nu{Ng9pzSP5tqG{blmVDvcvdzc*+Y4v==lDZuWCCG>C!BW=uoJR^0^@B0X;D{ z=O4N8fIK1v9*ANW*0Dl#Kp zIq+&*8Zy7^W#Vf;y9XY8>NkzwPq=q3>SFsBbMEI(jS;=CE!gxguJrdkCKQECUd^CX z$n?+*F%Fw7&U)fq5VQII_YMbR2o4UGR2Im6PWH5`BvB_2n?BhahDLD(8qMgjv*URA zAK$)itiWb9-hFe`H*s{Jw=_4vh9u3TO2A0M8=NNo)GtwQ)9pHWk z@a&9_kCTFMe30U?%5FG*5DLewGN5ogDJ{F|Zm~Yypl5Fgf~kAe44XT2n=qe4Llxu{ zT-?=S4(0r`4geRdR^c2L9v&X2qkO7{>;a~iuQL2W(RGedyk6Y($1M?`HHfm&6E0CQyMYit&>*spYGmuTvl*o`{)=a|GlX0ez z2_EeWtb1bqDk$?2=jf`S3UpXMAAF3D?hSwW8?{hY%DB zL~9m^A(*rkUpI`wWQDZ(>V;07%|;Ub7Crbif>bLV2? zCH#S@Q;V@Yh4}FHtY>4aXb-%#V?oq4Q0_i8V!S0r zj+~n9DoGk`?!m57!H@Q7%E9Qk5nu&XaW zIhT!}^23UR-D6^)XOkLM=nb1YevLxM)Cnd~a<%=^hIR^@!LJ z@E)EFtpFZ$BveHHycK|iC)rA(ePr4tmO>3in3MZKx}2U()&_PB&EVt1M~*-=jhA<~ zt*zvvBG{180v`f+{GBzC9ep4JeVzAzSWD~RwMG%p(l~1ETLF0x^f?=NsNUL({~MJjmzN1~#@Vll?>!bcjfHhqH?_m(IE^oCRR>JP?%p z6WFX1`VBUJz+h92qtn?~mD`5aSvp!T@M)(Z0YO6mrnJ8)O$Hdj zv(~Op!WO@AhpU-sbUhU27k)TgdOSK>8aGu;P$G7xd6EU3SVDb)CyN63N8!Z)e-sdr z9b!*nOtR`?kF(U!&>`_UujtTb(fvtjypbF^q?+jYl=>?M)1T$Ln(8MPOzgXNy|ex^ zdQ>oFZ4J>K5B3YrsU%?mWx)^nSx=SuLJsm$z~6S(@J~A{#tGTk_)3I*P~L$PT5L{m!YTp!RDQEHN=dH5KsJPvp5> zG#X<=XZ@N1su)uEpSuX`C1i`!fPr(Afk#UZ!NqzbpqT+h7C<0D4*hcXt0R9AE@@t5 zSKu=*^&j9PlYZ<=p&l4wY6HL27y$;wu5x!^V8EKt#ZG$tJ>U+QGZfV-H}_DNGUliG z?P(CN0(PNwex7T04%>?WyYPL)NXQdy;Dd{tYx{8-%!M6 zMkVu>>U-qDNe-P8t&yVH{-LJBw8)G2Bo*_tMce$XqvUv6t`F@^W6R=-zhUbeHOwtw zPIql()HscR`e4OGN7CbBd{aPs&s17n&Hy(+iSVAF3vv>sbJudzhAbn`IR-pHF!22k{;-7EMh} z{8^FSWl`~YuNxd3-gahp6*c(C=3cGg(Wh4hvGx-*dNwL^$wXmBH3L7War_9qh4XrW zG?C54uP*-tc9AjP^>erWk9(qF?t1?r{$64KJ4bZX>p73B##Q9~<+3*^Qbh+jKfa$r zV+9vo^K!mVokm!!XlEud_$z5>{h*&q`uGWn6YRF795=3GTvfQMca(71$)zi&V`Isz zWl3fvaNr4QT6x z&}Aj@)pQ8JZi16@9w4uY2^KzHUTM-OyG7yft{1}%Fc$}7!YU9;-Ljpgfi(3*7 zySD3J(?I9DQQ0O=rDzW_XNWFym&ieJyR2Iq85;*`W;*|x*Z$L7)N5dVf;xwHs4sgI zisXL-maG^p+Xf!Es^_^49@T=ows9VWtWLI$LLVPwk(%GYEMQT+B-_E7xIlp6b_ zr-1x7>i~n;G!O4Ux@sPP(c`jRXdnY4dH_bU9^PglM`K5V(oJQv;Iiq9f>5{~02hW) zo`qv|Hs~*>owTDcW~MCUD4KW4=V$gQW0wI9eVA*{!F|m`L zjNM5V#{nXb41w{soQ*lrmRwrpA9_~>1)jDIO-t|s%%?(T* z7l`5q*BVUdMwJR8OroY^u=S9bZg>4)GCQDo+kXX~dzO3PpE(0r zgWsHia|hrItkW%1#C&TKQ6AT0Vp1jeT1wqaOP-L)gg?@6@$YGQ#GgF_5O47>SL@{& zhgCfSlHsaLS391#-I>iILsaX{ywi>>>~gk=#x0!Fd{wG8vJ+(6Sb^sjDi#*Miq1N2 z+SQQwHXQDZ>a;;|>jr!e1@RxI>(H@--wjPtqX?iFEC{&bk+E6{ih-g z3D1gkcNCksd(XG%I6|9qH#j#l>tah{G}mX^#mzQoh>f63f!K%;lZvb|$=OVdS`+aD z&IO}mF*)$`yI%c|oBHU=q?j|!v@pUpQf z7AdtF*LjcY*w$lC0W2&t;aGY5W6AfwS=dzs9557OOm(`wY;6(&4Cgd z@;^x;%hQMt$4$jxS9$u||5CfT#|ndWZ;H5SXkb8Eg&O-!z5+s_FGKX!<6Zkic^Q3- zf&!3P&<(j>0N?v-yyV4;7XtN&@bfXo?SUS_!Id+##G0CHz$&QW})AX)a*TTVb5 zXr8{t#@^YGZ1YRL)cT=XSkYSnwZA-+Vuo65S67!nJz03%kyk&?q_mF9C1*8s0>xLb zOq{ZfQP?E^Qp56Jv4*&m6qZhRH$9}%vbjf{zQt`ui)H@xc;OKda~HA6uf^`X)pvbM zg)s8{<8v?p9GVrA&Fz zk5d7_pj44W4xR2=9}t#S?$7=|G|mt!NQGG1%~3%dz|Tv@u3m;y7cbKKsKvmTY}Y(| z0KC+AQv@$B?;*(U!8Fn?<0<(LHEgPQi8i(P7?p|DDjWund0&Le)lUL`*a$O012v|2 zz5qy#pAm$^hYw#3eZN;?y#n!XN{| zCOaVNMDgDxxgSjt(6u2{iYxLihOz1#_dF7oS60gVvKm&FJAn?=(JXbYuW0CvUV&H* zp%nSv%UiFH17;P10g~KPT2eNGqh|99&}x8a1G!%zb#9NVO({N^*Ma)7hv=Yg92Ooe zO4WSh32#Ex;5`tGek%Be%iDS+Esxt=F_x1%32VahPGF(#3*#AWUm=DP-UI${T37F- z@-D7;ESL$f1{664e=}r%mKBH{AlMfM7)1c20XVpfhGe}F2f)(d@D&?x&2<<kze-*K!4kHKP02~A%ZTBf)N#tI$oomh`&c-= z<>iZ))n+(5Cb?M4sftbkj3I zJ=hc}T`d-czL80YRYMY;R|T@i)<4Yr9%20@QmWw2D4KJtmKSs>EEA zXMADSg^%wM5GM9!+!!j-G+jHyr3i>2ABCtr1{z>7*kg02#)^);((CI3uX^=|PofyW zFl}w}Mg|6=-IfCg!Ub0ux^%}m7e~7p$GuPF%x zCewlJ2v1M1$*y$OKPyB6YEayw*n&bhBn<{@{WzoVc6BwAHBd`BV@ z*Ko0pr)!i>WDg#7qOzZOkWz0pt7+EK&6Bms>!&l(88b%}(fecO7g8m5a$Gl#S+eN} z4KD0uiRF(|*_du(Dqoq$BiAxC=Ol%qQp$Kga!_?#`7ru1?jFfl!qL(+n>R19_W0sL zLwb2Fp}Jw(RC^8ywkb?gCfkuK-IfVyzsMeetQ>#swjzv-IVC%lGv)LUr`&! zBb$3N6>#{4#SACTX}zON3=b=3&LxWPlPo_T%PMKGzxrA#(lxfFoardez00_L6}PH3 z;gmKC>Q+gdMh<|=Pr$-5HpGg~MUL*TPGXWlGX9)=rtHA^YAVa6p?Gku@4VP^C!x73 zC(b>KTo$aOwO^{{9V$(~Scj~*$B72)52%;q1-iqI(oH5uEy?f6hmTWbG^;V(mwe0- zHt6Djv`g;wb7FY2s>UKz_arh?=!A3HD0puswBB)a2=|rOqGhAYlV}`KH$+6R5551c zZLKhF+BC7u_oF$eE3E%g!~6Y7xU7D~YVo8}V9-rdkR%)+}&G>1GnRdea|zVI%^hErbdc013?b~k6^*K^6O9EOtI z4^D~iZjS2~V80$=+A|7JI^YSeEBMm&3{k-0TH6k4JD{)bT!)4v0P>`exdODlJb_WkLZUm?}a~H9}R`33v9%h%1 zi$rO|z-Bjj3_C11>SFSV9pQo2TlW-vQ9d}^2*;yUaBTPYSO3EZJjhmb>tML$e(2w% zHJ~yLl-{URBwc{eUk?0n4*hSOHw?3aAn`nE8Un**L0FF?`O@wY_Rfh4fi{l{}`~|>k_Mpa4LnZ=Shw|_hN7|HqM!-Z1^1& zJErv_bMi3Qez21PFFYXe=?K|H_q_jh(JDZrK`S?Q5?VR;^N3!r5g^Fh_}Ar}h}m@? z&;HF9)r0-9CzgOG8i*FWORT_u6F*oR6Q1w?R^m~s_(hFbM%0pd( z$p~6_jZtFatFX1Ir-CN-{|b}#-(3`{bDp4y1p*qF5d6p0{A2IApZblQcZUGY{DZMQ zy}G1ichn!x@IOD^weCruHSRCGB4x}+-;<| zDX%@%Ei=xx`$G}#Wx`b}YQ#m*1@cz5i~t(EcnA7#jKDamZMXHJC{P$cFPQRV-0jo| zQN&R1$5_DsLY)}_LG>K8ZG_mT+u+ZTeh#_<#1BH<9FKwQ(f=PmkJH>!yFmB+`Mr( z_&KK=)X)al6sX%~JBZJ_?QBY4f-aSzt_dLIg6>t~_~$nu=@5F?FG3$08oovQwh~Ik zKWfcrX_Hb3aqO)V!52L}YYAupM(~yn_%X6(QKumI1x>!R6zCO$!vRJsP3r}gVfRn0 zXy5M_N|#XDW%BKGh+lPgPVO!_$nUPrnkj-99bQirpuYp*kdP8@QVA3h!_PRlYZ-S( z;Yn7JBuY-j#^DCvm>tTJ0^$Z*?Zkv*R8b4imw$iAfA*QMQiX*3(O5F-CYogU7D!MH z!hh!kb(8wuAI@8HfVQCsRt#i;poPx!^#WROqJ7p++P%Mgw&XCd6cra#0(o9+F#9WL zOuX)7tkA!qJ--j=8-FeyfuHb$)t${~@1mxGUy|40KcGCk3w0mw4se)enVbsrtA%gV z0ShAGnFbZVY!!(Y;Qt2j4*K|iyTxg4q1lGo*_*&#$h2kNnhH~#Fm8{2w)S&6#K5rg z@FY`GSrDqJ_^oT*lLhwVZv zq^$%Rh20rhqb1n%k1;c-O5u;(o1AJofmv-Z{ikODyxLe0pigL7=$%LO_<6o)$Hk|H zAO;;{5<@sFz7g%ud684lgBRdwaSJbUol8^cYHDBXwQuT1t6z+`6(t}tT)NU2h9ngubPp=G(nmS?xpDrObjDyLtr^&uom z*y+nrYPky3eYx^qi57iaPL>{{cJ?6S&PBdhERLy&lsL zPW>U#LYrW)&fe;bMOk7Eu`cq`g3B37Zu1 zZR7I2d-p!oFkZ|Oaag+9VVT$zmdP2hHui98Vk&@m%NL!|BUY@9FUItNmz7PLA9-pv=;_U@=^SWeNBiEbV@jo2eBq^S7el;@ zmn_O$v-0}R*W~LXMJ(n0Fza~@miVliDM%EJ9|W3)`JA}x2LrnHUedf6N8lGu^P`E=S6zeM|~rB7=z z*5mWJyw_|`G$Bn?B(Cf654t{|8T)K4Dn8p6bC5R)wc*ARDR#scm!oag#|X`{#R)3f zL%pSrZ|~nP+(sRAA>ic{+D<%3$ShZv5h{0Swxm!0xxrH~J!ss2;@FUZ7uk&e+-~t> zio4~VvOuw#Cn{z3H_~stza_GTI<>`hdJ7fMB+C%Xn2h%d)_#;Ku`+6YKqA3|8f>|#A@`qCU1;!@FDXP7vT3@#`*2|`KWXs@X3+#1sUS8o1;moiO zR-`tr4(Z*jsBr9ghSBR+}9Q ztZup_O&VoNtU4v1supdxf^oIr`Srk#DzRqu%7|xdn+&0SaAU|-c&=2Ijw7^Nm!@+xAn-<%mXKif4)VH>8;8{yF)e6F8WBB#i>Q)~)?+lCo zIwVPfBdZIE`YoCJ>ZYrJXvEU73UmRGYSZQbvRJ2OkGB zewYRr|NOn_T&1>d;7X_eVG-VJM(;Vg=5Ro({fUk5QWOlaP-PP zhP`QOd9Ho;NVBuE^JhEr+)M;gjjl35Gk%#aY-jVCxr)3=pxBTv$iriK0+ajm>AHF! z-+5t=zmeOO%T{`i8BJQ88xl|2>)g%ap?`+)678X5R4bBRd{f#g(L!Lmoy~N)oljS2 zIe}b%NlGHpA7oNd=-ZN`-ts->P05bFKVJ{7Oi!l0IIzVO8C$hfNLf-b^C~e}eW5^n zJ#Xj+&N-j1?;CE`rktSr1+Y;*^b64sK2Vv8UbmzNS!u4O#n%f6XXH+vm3 zi#f|3YBTs#&S=`A2OgK%Vc_?ze8>$Ow3hs_GDXw2G(40*m zU;I?DN25|p8dtWC`Ks$S=>&3=7}%pS*U3W=>Q$F}0XNs@A|MaIl#|QQkyx!#YDmf4 zCf!{yb3rHG$2{MjNRySP@){sU7|l3)42#^LymOd*|K0W9#gX9qhm&5chx* zU0V_#LTvhC2PS^CBd!m5q&9T&Zz-0xp5H|AI0e{Q7G^iV@|Rx>yxqFe#i~hXrHKh8 zRE77xL_4f*3vPdFOdY#OF^dZ?fG1$XLakE{@4d5@r7kCg1M#KBPs$VsEGMHPHn>;a z$?){|Hn1nTLj)N$o<(mXW1zk$prXHuv=p!^E0wuee+7Pf4U?E`Sx|B&&r%cZ|P;L425KuyF;2F3WLYQ z29K#?UZM^K%#OU*9HliDI$C?t`Z=+|i767I{BcsSH`1;o;q5QPE8$HuA#X8CcHgl>^q$n37zb1#Q;$lw()#X{@iI&U6EtaOrHig>RkCzEEM8~q2 zotK#*Cf@G7XLi>k7CmK`9oAv>@QYGEsM#Mkx7-nfmx=ndVEx_Kv`u$YeCAFCpKNOP z{cbwU++2T7Bca%Ry<`9QV4G3|oxp^}+oD;*s%z=%bBMuymPP=`nr_#h2u#f<0MWTq>ViJM{KS9@WH@eA*xG6&w?7lGm%B<8WYZ@($eQc`lau5Zf(_E z8ND;2D4i_RUANmW{olGSE@JzG6@1>EHM2^AA%^~qvOdk1(Q2cPTu za$cCMT8HIe0|Nr?4e7Xs{R%*QL2ErQc;2Ngz%gAq9DwDl23$ynZ(R=z415Gsxn!>? zPGfcC%q!Rxq9I8%!rI<_2T0P+iX7<;pte$G%>PIk9leIyqi^?nVAZZ1@@^e2-{R(vSwJtTgEKDQk8UCRxH>9*jXry~9au z^{<_gmZXc>cHBlz1owGmDlJ>D^x++GCg-v;z+iQJkOnKG-U3Bld->aS9?s6093_*D zTNLaQxarhO7ZBE!f>5oGNfUJ!?g0B$DUVq!6jfNzFr~1c9MJn^%vPF=`Rfm;zzZ#m zPX8n{tG*gy${8VX37s;CS!1Zw=YkR8)%Ng*?_*g%n-gdJb)BY6He2g_gQQR^!JrS2 zAD(ui{T*EXQO6nExF?N23pX+A91jmI>U+wA8J1Z-JuYl`*vu5>7;D?6pNA_DoF#{C z{;lPO)Z|tOF5BUTLY5YyPUY1wKj}k<$D_jCvfbM$tcr;SC&uza8%{gCZ)6fRud4$m z*6dXo@_!ks%H1>jn;6$hdKeR1MK7Y08NxY~BRqQ=TRPrzjZb1A+sPytpK>#j>)+>b zE9Sz&fk7Fa1xX=BF({ap18f+?`OOL%?9dW{N}IAdTzp!_2Yb7PAe)OP$ePBtK<57d zg878uaW*a=PY8WzS3gzBgXr+VO(Y~aBZ=?y#3ZhUG$9!2ZN=02?y3GNZ7W&>luPMJ|pNd4B)g}^T<>V>pn2zg=CU1-$ zUy)F$`lAY2wKCQ(x_pG)SOpUk_dwoV4U{-iHSMW{tPk;QUoN&mLDR5#??z2d))4S& z`PfB$(E3P=31BQmJfSybPe+s&afC&BYflsi6v@Ls z9L6MO56^dgPo_Q}2Xd94L)!QwlLj@U`|6x;K9(y$v$6ZL?U1SSsGQ@Kgu#e5SR`xq zF@3{s(ZN}W8%|7C&|(|sRl!kl6#<^Q2;8rpGF!TzGxvix8a%V@Z6$6{1dBOP8W<#{ z=Xor%SVCt)XX#BR#MkIkOa<9{oLKjP=I^zcI|RKG8-0TSw71g*LIdMQ~AwU-->+m zxgKAr>ad2$ilXR@8j(sAT0eKtnP(iyR5xAq@f(>pPx>Ui+)M2e2c;%+TDkmjnD0{| ZeEJ;cY~|xauS6v+8%ukOYIE;|{{iQH0n`8h literal 0 HcmV?d00001 diff --git a/doc/tutorials/introduction/java_eclipse/images/11-the-code.png b/doc/tutorials/introduction/java_eclipse/images/11-the-code.png new file mode 100644 index 0000000000000000000000000000000000000000..917e76b8ec0aedb6e24d9a605bae17feab3a35ec GIT binary patch literal 34150 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 EU)P#^3IG5A literal 0 HcmV?d00001 diff --git a/doc/tutorials/introduction/java_eclipse/images/2-user-library-new.png b/doc/tutorials/introduction/java_eclipse/images/2-user-library-new.png new file mode 100644 index 0000000000000000000000000000000000000000..e65faef2df4a50a9ab1c326cba23be44271c5753 GIT binary patch literal 48175 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

?v`V$9#GF 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 literal 0 HcmV?d00001 diff --git a/doc/tutorials/introduction/java_eclipse/images/3-library-name.png b/doc/tutorials/introduction/java_eclipse/images/3-library-name.png new file mode 100644 index 0000000000000000000000000000000000000000..bc79cde80b2d5fd2fe93a937548599399a8c517d GIT binary patch literal 18880 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 q@*AEP)z@)`(s^epWYBQcZ;1BoR?oUnK}4 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 literal 0 HcmV?d00001 diff --git a/doc/tutorials/introduction/java_eclipse/images/5-native-library.png b/doc/tutorials/introduction/java_eclipse/images/5-native-library.png new file mode 100644 index 0000000000000000000000000000000000000000..a0a4962e98f0bb5a5c3ed553f0d662cc0cbf8826 GIT binary patch literal 50587 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 UMIQ)!%kiI^ntB>V&__Z42W7nFssI20 literal 0 HcmV?d00001 diff --git a/doc/tutorials/introduction/java_eclipse/images/7-user-library-final.png b/doc/tutorials/introduction/java_eclipse/images/7-user-library-final.png new file mode 100644 index 0000000000000000000000000000000000000000..67b15daba1f19819f51889cb98e4252ad70de36f GIT binary patch literal 47479 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 literal 0 HcmV?d00001 diff --git a/doc/tutorials/introduction/java_eclipse/images/7_5-new-java-project.png b/doc/tutorials/introduction/java_eclipse/images/7_5-new-java-project.png new file mode 100644 index 0000000000000000000000000000000000000000..8aaef5d4ed02c5df8253f1ee482edf7b889b0674 GIT binary patch literal 23680 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 R_(1qy3J+Bt6u}Yx{{@;w9qa%A literal 0 HcmV?d00001 diff --git a/doc/tutorials/introduction/java_eclipse/images/8-add-library.png b/doc/tutorials/introduction/java_eclipse/images/8-add-library.png new file mode 100644 index 0000000000000000000000000000000000000000..b13c65c7ae0b2017980f0791e41b450288eec7e0 GIT binary patch literal 42696 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 literal 0 HcmV?d00001 diff --git a/doc/tutorials/introduction/java_eclipse/images/9-select-user-lib.png b/doc/tutorials/introduction/java_eclipse/images/9-select-user-lib.png new file mode 100644 index 0000000000000000000000000000000000000000..1c80e9821880f8b6b06430ec73c67bed660e054a GIT binary patch literal 35428 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. In this tutorial I will explain how to setup development environment for using OpenCV Java with Eclipse in **Windows**, so you can enjoy the benefits of garbage collected, very refactorable (rename variable, extract method and whatnot) modern language that enables you to write code with less effort and make less mistakes. Here we go. + + +Configuring Eclipse +=================== + +First, obtain a fresh release of OpenCV `from download page `_ and extract it under a simple location like ``C:\OpenCV-2.4.6\``. I am using version 2.4.6, but the steps are more or less the same for other versions. + +Now, we will define OpenCV as a user library in Eclipse, so we can reuse the configuration for any project. Launch Eclipse and select :guilabel:`Window --> Preferences` from the menu. + +.. image:: images/1-window-preferences.png + :alt: Eclipse preferences + :align: center + +Navigate under :guilabel:`Java --> Build Path --> User Libraries` and click :guilabel:`New...`. + +.. image:: images/2-user-library-new.png + :alt: Creating a new library + :align: center + +Enter a name, e.g. ``OpenCV-2.4.6``, for your new library. + +.. image:: images/3-library-name.png + :alt: Naming the new library + :align: center + +Now select your new user library and click :guilabel:`Add External JARs...`. + +.. image:: images/4-add-external-jars.png + :alt: Adding external jar + :align: center + +Browse through ``C:\OpenCV-2.4.6\build\java\`` and select ``opencv-246.jar``. After adding the jar, extend the :guilabel:`opencv-246.jar` and select :guilabel:`Native library location` and press :guilabel:`Edit...`. + +.. image:: images/5-native-library.png + :alt: Selecting native library location 1 + :align: center + +Select :guilabel:`External Folder...` and browse to select the folder ``C:\OpenCV-2.4.6\build\java\x64``. If you have a 32-bit system you need to select the ``x86`` folder instead of ``x64``. + +.. image:: images/6-external-folder.png + :alt: Selecting native library location 2 + :align: center + +Your user library configuration should look like this: + +.. image:: images/7-user-library-final.png + :alt: Selecting native library location 2 + :align: center + + +Testing the configuration on a new Java project +===================================================== + +Now start creating a new Java project. + +.. image:: images/7_5-new-java-project.png + :alt: Creating new Java project + :align: center + +On the :guilabel:`Java Settings` step, under :guilabel:`Libraries` tab, select :guilabel:`Add Library...` and select :guilabel:`OpenCV-2.4.6`, then click :guilabel:`Finish`. + +.. image:: images/8-add-library.png + :alt: Adding user defined library 1 + :align: center + +.. image:: images/9-select-user-lib.png + :alt: Adding user defined library 2 + :align: center + + +Libraries should look like this: + +.. image:: images/10-new-project-created.png + :alt: Adding user defined library + :align: center + + +Now you have created and configured a new Java project it is time to test it. Create a new java file. Here is a starter code for your convenience: + +.. code-block:: java + + import org.opencv.core.Core; + import org.opencv.core.CvType; + import org.opencv.core.Mat; + + public class Hello + { + public static void main( String[] args ) + { + System.loadLibrary( Core.NATIVE_LIBRARY_NAME ); + Mat mat = Mat.eye( 3, 3, CvType.CV_8UC1 ); + System.out.println( "mat = " + mat.dump() ); + } + } + +When you run the code you should see 3x3 identity matrix as output. + +.. image:: images/11-the-code.png + :alt: Adding user defined library + :align: center + +That is it, whenever you start a new project just add the OpenCV user library that you have defined to your project and you are good to go. Enjoy your powerful, less painful development environment :) \ No newline at end of file diff --git a/doc/tutorials/introduction/table_of_content_introduction/images/eclipse-logo.png b/doc/tutorials/introduction/table_of_content_introduction/images/eclipse-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..64ec01c2534d34fbda3b02cef963da9d5e92ff9b GIT binary patch literal 6162 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 KvbEBtVgCokPsYRm literal 0 HcmV?d00001 diff --git a/doc/tutorials/introduction/table_of_content_introduction/table_of_content_introduction.rst b/doc/tutorials/introduction/table_of_content_introduction/table_of_content_introduction.rst index 2238b87878..ee8dd67ef0 100644 --- a/doc/tutorials/introduction/table_of_content_introduction/table_of_content_introduction.rst +++ b/doc/tutorials/introduction/table_of_content_introduction/table_of_content_introduction.rst @@ -138,6 +138,24 @@ world of the OpenCV. :height: 90pt :width: 90pt + .. tabularcolumns:: m{100pt} m{300pt} + .. cssclass:: toctableopencv + + ================ ================================================= + |EclipseLogo| **Title:** :ref:`Java_Eclipse` + + *Compatibility:* > OpenCV 2.4.4 + + *Author:* |Author_BarisD| + + A tutorial on how to use OpenCV Java with Eclipse. + + ================ ================================================= + + .. |EclipseLogo| image:: images/eclipse-logo.png + :height: 90pt + :width: 90pt + * **Android** .. tabularcolumns:: m{100pt} m{300pt} @@ -295,6 +313,7 @@ world of the OpenCV. ../windows_visual_studio_Opencv/windows_visual_studio_Opencv ../windows_visual_studio_image_watch/windows_visual_studio_image_watch ../desktop_java/java_dev_intro + ../java_eclipse/java_eclipse ../android_binary_package/android_dev_intro ../android_binary_package/O4A_SDK ../android_binary_package/dev_with_OCV_on_Android From e7921c3fe765a1d230abdd6aaa291d4a0c1bac65 Mon Sep 17 00:00:00 2001 From: StevenPuttemans Date: Fri, 30 Aug 2013 13:16:18 +0200 Subject: [PATCH 04/68] Added changes suggested by bug #3186 : fixing the unplugging of webcam --- modules/highgui/src/cap_v4l.cpp | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/modules/highgui/src/cap_v4l.cpp b/modules/highgui/src/cap_v4l.cpp index 045c6f889c..49432c984a 100644 --- a/modules/highgui/src/cap_v4l.cpp +++ b/modules/highgui/src/cap_v4l.cpp @@ -1236,10 +1236,10 @@ static int read_frame_v4l2(CvCaptureCAM_V4L* capture) { //set timestamp in capture struct to be timestamp of most recent frame capture->timestamp = buf.timestamp; - return 1; + return 2; } -static void mainloop_v4l2(CvCaptureCAM_V4L* capture) { +static int mainloop_v4l2(CvCaptureCAM_V4L* capture) { unsigned int count; count = 1; @@ -1273,8 +1273,13 @@ static void mainloop_v4l2(CvCaptureCAM_V4L* capture) { break; } - if (read_frame_v4l2 (capture)) - break; + int readresult = read_frame_v412(capture); + if (readresult == 2){ + return 0; + } + if (readresult){ + return 1; + } } } } @@ -1354,7 +1359,10 @@ static int icvGrabFrameCAM_V4L(CvCaptureCAM_V4L* capture) { { // skip first frame. it is often bad -- this is unnotied in traditional apps, // but could be fatal if bad jpeg is enabled - mainloop_v4l2(capture); + if(!mainloop_v4l2(capture)){ + fprintf( stderr, "HIGHGUI ERROR: V4L: Could not capture image.\n"); + return 0; + } } #endif @@ -1366,9 +1374,10 @@ static int icvGrabFrameCAM_V4L(CvCaptureCAM_V4L* capture) { if (V4L2_SUPPORT == 1) { - - mainloop_v4l2(capture); - + if(!mainloop_v4l2(capture)){ + fprintf( stderr, "HIGHGUI ERROR: V4L: Could not capture image.\n"); + return 0; + } } #endif /* HAVE_CAMV4L2 */ #if defined(HAVE_CAMV4L) && defined(HAVE_CAMV4L2) From 2d5a1dacd09845c7e18d8e4373c50b8af60d9ab7 Mon Sep 17 00:00:00 2001 From: Jin Ma Date: Mon, 9 Sep 2013 16:08:23 +0800 Subject: [PATCH 05/68] Added Kalman Filter of OpenCL version. --- modules/ocl/include/opencv2/ocl/ocl.hpp | 32 ++++ modules/ocl/src/arithm.cpp | 60 ++++++++ modules/ocl/src/kalman.cpp | 135 +++++++++++++++++ modules/ocl/src/opencl/arithm_setidentity.cl | 100 +++++++++++++ modules/ocl/test/test_kalman.cpp | 147 +++++++++++++++++++ 5 files changed, 474 insertions(+) create mode 100644 modules/ocl/src/kalman.cpp create mode 100644 modules/ocl/src/opencl/arithm_setidentity.cl create mode 100644 modules/ocl/test/test_kalman.cpp diff --git a/modules/ocl/include/opencv2/ocl/ocl.hpp b/modules/ocl/include/opencv2/ocl/ocl.hpp index 5cd69b3163..7ceb2183ec 100644 --- a/modules/ocl/include/opencv2/ocl/ocl.hpp +++ b/modules/ocl/include/opencv2/ocl/ocl.hpp @@ -587,6 +587,7 @@ namespace cv CV_EXPORTS void cvtColor(const oclMat &src, oclMat &dst, int code , int dcn = 0); + CV_EXPORTS void setIdentity(oclMat& src, double val); //////////////////////////////// Filter Engine //////////////////////////////// /*! @@ -1847,6 +1848,37 @@ namespace cv oclMat bgmodelUsedModes_; //keep track of number of modes per pixel }; + + /*!***************Kalman Filter*************!*/ + class CV_EXPORTS KalmanFilter + { + public: + KalmanFilter(); + //! the full constructor taking the dimensionality of the state, of the measurement and of the control vector + KalmanFilter(int dynamParams, int measureParams, int controlParams=0, int type=CV_32F); + //! re-initializes Kalman filter. The previous content is destroyed. + void init(int dynamParams, int measureParams, int controlParams=0, int type=CV_32F); + + const oclMat& predict(const oclMat& control=oclMat()); + const oclMat& correct(const oclMat& measurement); + + oclMat statePre; //!< predicted state (x'(k)): x(k)=A*x(k-1)+B*u(k) + oclMat statePost; //!< corrected state (x(k)): x(k)=x'(k)+K(k)*(z(k)-H*x'(k)) + oclMat transitionMatrix; //!< state transition matrix (A) + oclMat controlMatrix; //!< control matrix (B) (not used if there is no control) + oclMat measurementMatrix; //!< measurement matrix (H) + oclMat processNoiseCov; //!< process noise covariance matrix (Q) + oclMat measurementNoiseCov;//!< measurement noise covariance matrix (R) + oclMat errorCovPre; //!< priori error estimate covariance matrix (P'(k)): P'(k)=A*P(k-1)*At + Q)*/ + oclMat gain; //!< Kalman gain matrix (K(k)): K(k)=P'(k)*Ht*inv(H*P'(k)*Ht+R) + oclMat errorCovPost; //!< posteriori error estimate covariance matrix (P(k)): P(k)=(I-K(k)*H)*P'(k) + private: + oclMat temp1; + oclMat temp2; + oclMat temp3; + oclMat temp4; + oclMat temp5; + }; } } #if defined _MSC_VER && _MSC_VER >= 1200 diff --git a/modules/ocl/src/arithm.cpp b/modules/ocl/src/arithm.cpp index 11e9c50f43..819c013909 100644 --- a/modules/ocl/src/arithm.cpp +++ b/modules/ocl/src/arithm.cpp @@ -98,6 +98,7 @@ namespace cv extern const char *arithm_phase; extern const char *arithm_pow; extern const char *arithm_magnitudeSqr; + extern const char *arithm_setidentity; //extern const char * jhp_transpose_kernel; int64 kernelrealtotal = 0; int64 kernelalltotal = 0; @@ -2342,3 +2343,62 @@ void cv::ocl::pow(const oclMat &x, double p, oclMat &y) arithmetic_pow_run(x, p, y, kernelName, &arithm_pow); } +void cv::ocl::setIdentity(oclMat& src, double scalar) +{ + CV_Assert(src.empty() == false && src.rows == src.cols); + CV_Assert(src.type() == CV_32SC1 || src.type() == CV_32FC1); + int src_step = src.step/src.elemSize(); + Context *clCxt = Context::getContext(); + size_t local_threads[] = {16, 16, 1}; + size_t global_threads[] = {src.cols, src.rows, 1}; + + string kernelName = "setIdentityKernel"; + if(src.type() == CV_32FC1) + kernelName += "_F1"; + else if(src.type() == CV_32SC1) + kernelName += "_I1"; + else + { + kernelName += "_D1"; + if(!(clCxt->supportsFeature(Context::CL_DOUBLE))) + { + oclMat temp; + src.convertTo(temp, CV_32FC1); + temp.copyTo(src); + } + + } + + + vector > args; + args.push_back( make_pair( sizeof(cl_mem), (void *)&src.data )); + args.push_back( make_pair( sizeof(cl_int), (void *)&src.rows)); + args.push_back( make_pair( sizeof(cl_int), (void *)&src.cols)); + args.push_back( make_pair( sizeof(cl_int), (void *)&src_step )); + + int scalar_i = 0; + float scalar_f = 0.0f; + if(clCxt->supportsFeature(Context::CL_DOUBLE)) + { + if(src.type() == CV_32SC1) + { + scalar_i = (int)scalar; + args.push_back(make_pair(sizeof(cl_int), (void*)&scalar_i)); + }else + args.push_back(make_pair(sizeof(cl_double), (void*)&scalar)); + } + else + { + if(src.type() == CV_32SC1) + { + scalar_i = (int)scalar; + args.push_back(make_pair(sizeof(cl_int), (void*)&scalar_i)); + }else + { + scalar_f = (float)scalar; + args.push_back(make_pair(sizeof(cl_float), (void*)&scalar_f)); + } + } + + openCLExecuteKernel(clCxt, &arithm_setidentity, kernelName, global_threads, local_threads, args, -1, -1); +} diff --git a/modules/ocl/src/kalman.cpp b/modules/ocl/src/kalman.cpp new file mode 100644 index 0000000000..2ee7f96567 --- /dev/null +++ b/modules/ocl/src/kalman.cpp @@ -0,0 +1,135 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2010-2012, Multicoreware, Inc., all rights reserved. +// Copyright (C) 2010-2012, Advanced Micro Devices, Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// @Authors +// Jin Ma, jin@multicorewareinc.com +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other oclMaterials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors as is and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ +#include "precomp.hpp" + +using namespace std; +using namespace cv; +using namespace cv::ocl; + +KalmanFilter::KalmanFilter() +{ + +} + +KalmanFilter::KalmanFilter(int dynamParams, int measureParams, int controlParams, int type) +{ + init(dynamParams, measureParams, controlParams, type); +} + +void KalmanFilter::init(int DP, int MP, int CP, int type) +{ + CV_Assert( DP > 0 && MP > 0 ); + CV_Assert( type == CV_32F || type == CV_64F ); + CP = cv::max(CP, 0); + + statePre.create(DP, 1, type); + statePre.setTo(Scalar::all(0)); + + statePost.create(DP, 1, type); + statePost.setTo(Scalar::all(0)); + + transitionMatrix.create(DP, DP, type); + setIdentity(transitionMatrix, 1); + + processNoiseCov.create(DP, DP, type); + setIdentity(processNoiseCov, 1); + + measurementNoiseCov.create(MP, MP, type); + setIdentity(measurementNoiseCov, 1); + + measurementMatrix.create(MP, DP, type); + measurementMatrix.setTo(Scalar::all(0)); + + errorCovPre.create(DP, DP, type); + errorCovPre.setTo(Scalar::all(0)); + + errorCovPost.create(DP, DP, type); + errorCovPost.setTo(Scalar::all(0)); + + gain.create(DP, MP, type); + gain.setTo(Scalar::all(0)); + + if( CP > 0 ) + { + controlMatrix.create(DP, CP, type); + controlMatrix.setTo(Scalar::all(0)); + } + else + controlMatrix.release(); + + temp1.create(DP, DP, type); + temp2.create(MP, DP, type); + temp3.create(MP, MP, type); + temp4.create(MP, DP, type); + temp5.create(MP, 1, type); +} + +CV_EXPORTS const oclMat& KalmanFilter::predict(const oclMat& control) +{ + gemm(transitionMatrix, statePost, 1, oclMat(), 0, statePre); + oclMat temp; + + if(control.data) + gemm(controlMatrix, control, 1, statePre, 1, statePre); + gemm(transitionMatrix, errorCovPost, 1, oclMat(), 0, temp1); + gemm(temp1, transitionMatrix, 1, processNoiseCov, 1, errorCovPre, GEMM_2_T); + statePre.copyTo(statePost); + return statePre; +} + +CV_EXPORTS const oclMat& KalmanFilter::correct(const oclMat& measurement) +{ + CV_Assert(measurement.empty() == false); + gemm(measurementMatrix, errorCovPre, 1, oclMat(), 0, temp2); + gemm(temp2, measurementMatrix, 1, measurementNoiseCov, 1, temp3, GEMM_2_T); + Mat temp; + solve(Mat(temp3), Mat(temp2), temp, DECOMP_SVD); + temp4.upload(temp); + gain = temp4.t(); + gemm(measurementMatrix, statePre, -1, measurement, 1, temp5); + gemm(gain, temp5, 1, statePre, 1, statePost); + gemm(gain, temp2, -1, errorCovPre, 1, errorCovPost); + return statePost; +} \ No newline at end of file diff --git a/modules/ocl/src/opencl/arithm_setidentity.cl b/modules/ocl/src/opencl/arithm_setidentity.cl new file mode 100644 index 0000000000..0604ae81dd --- /dev/null +++ b/modules/ocl/src/opencl/arithm_setidentity.cl @@ -0,0 +1,100 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2010-2012, Multicoreware, Inc., all rights reserved. +// Copyright (C) 2010-2012, Advanced Micro Devices, Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// @Authors +// Jin Ma jin@multicorewareinc.com +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other oclMaterials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors as is and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ +#if defined (DOUBLE_SUPPORT) +#ifdef cl_khr_fp64 +#pragma OPENCL EXTENSION cl_khr_fp64:enable +#elif defined (cl_amd_fp64) +#pragma OPENCL EXTENSION cl_amd_fp64:enable +#endif +#endif + + +#if defined (DOUBLE_SUPPORT) +#define DATA_TYPE double +#else +#define DATA_TYPE float +#endif + +__kernel void setIdentityKernel_F1(__global float* src, int src_row, int src_col, int src_step, DATA_TYPE scalar) +{ + int x = get_global_id(0); + int y = get_global_id(1); + + if(x < src_col && y < src_row) + { + if(x == y) + src[y * src_step + x] = scalar; + else + src[y * src_step + x] = 0 * scalar; + } +} + +__kernel void setIdentityKernel_D1(__global DATA_TYPE* src, int src_row, int src_col, int src_step, DATA_TYPE scalar) +{ + int x = get_global_id(0); + int y = get_global_id(1); + + if(x < src_col && y < src_row) + { + if(x == y) + src[y * src_step + x] = scalar; + else + src[y * src_step + x] = 0 * scalar; + } +} + +__kernel void setIdentityKernel_I1(__global int* src, int src_row, int src_col, int src_step, int scalar) +{ + int x = get_global_id(0); + int y = get_global_id(1); + + if(x < src_col && y < src_row) + { + if(x == y) + src[y * src_step + x] = scalar; + else + src[y * src_step + x] = 0 * scalar; + } +} diff --git a/modules/ocl/test/test_kalman.cpp b/modules/ocl/test/test_kalman.cpp new file mode 100644 index 0000000000..db3f19e872 --- /dev/null +++ b/modules/ocl/test/test_kalman.cpp @@ -0,0 +1,147 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// Copyright (C) 2010-2012, Multicoreware, Inc., all rights reserved. +// Copyright (C) 2010-2012, Advanced Micro Devices, Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// @Authors +// Jin Ma, jin@multicorewareinc.com +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other oclMaterials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#include "test_precomp.hpp" +#ifdef HAVE_OPENCL +using namespace cv; +using namespace cv::ocl; +using namespace cvtest; +using namespace testing; +using namespace std; + +////////////////////////////////////////////////////////////////////////// +PARAM_TEST_CASE(Kalman, int, int) +{ + int size_; + int iteration; + virtual void SetUp() + { + size_ = GET_PARAM(0); + iteration = GET_PARAM(1); + } +}; + +TEST_P(Kalman, Accuracy) +{ + const int Dim = size_; + const int Steps = iteration; + const double max_init = 1; + const double max_noise = 0.1; + + cv::RNG &rng = TS::ptr()->get_rng(); + + Mat sample_mat(Dim, 1, CV_32F), temp_mat; + oclMat Sample(Dim, 1, CV_32F); + oclMat Temp(Dim, 1, CV_32F); + Mat Temp_cpu(Dim, 1, CV_32F); + + Size size(Sample.cols, Sample.rows); + + sample_mat = randomMat(rng, size, Sample.type(), -max_init, max_init, false); + Sample.upload(sample_mat); + + //ocl start + cv::ocl::KalmanFilter kalman_filter_ocl; + kalman_filter_ocl.init(Dim, Dim); + + cv::ocl::setIdentity(kalman_filter_ocl.errorCovPre, 1); + cv::ocl::setIdentity(kalman_filter_ocl.measurementMatrix, 1); + cv::ocl::setIdentity(kalman_filter_ocl.errorCovPost, 1); + + kalman_filter_ocl.measurementNoiseCov.setTo(Scalar::all(0)); + kalman_filter_ocl.statePre.setTo(Scalar::all(0)); + kalman_filter_ocl.statePost.setTo(Scalar::all(0)); + + kalman_filter_ocl.correct(Sample); + //ocl end + + //cpu start + cv::KalmanFilter kalman_filter_cpu; + + kalman_filter_cpu.init(Dim, Dim); + + cv::setIdentity(kalman_filter_cpu.errorCovPre, 1); + cv::setIdentity(kalman_filter_cpu.measurementMatrix, 1); + cv::setIdentity(kalman_filter_cpu.errorCovPost, 1); + + kalman_filter_cpu.measurementNoiseCov.setTo(Scalar::all(0)); + kalman_filter_cpu.statePre.setTo(Scalar::all(0)); + kalman_filter_cpu.statePost.setTo(Scalar::all(0)); + + kalman_filter_cpu.correct(sample_mat); + //cpu end + //test begin + for(int i = 0; i Date: Mon, 9 Sep 2013 17:00:33 +0800 Subject: [PATCH 06/68] Removed whitespace. --- modules/ocl/src/kalman.cpp | 6 +++--- modules/ocl/test/test_kalman.cpp | 5 ++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/modules/ocl/src/kalman.cpp b/modules/ocl/src/kalman.cpp index 2ee7f96567..8a5b0d4c2c 100644 --- a/modules/ocl/src/kalman.cpp +++ b/modules/ocl/src/kalman.cpp @@ -48,7 +48,7 @@ using namespace std; using namespace cv; using namespace cv::ocl; -KalmanFilter::KalmanFilter() +KalmanFilter::KalmanFilter() { } @@ -66,7 +66,7 @@ void KalmanFilter::init(int DP, int MP, int CP, int type) statePre.create(DP, 1, type); statePre.setTo(Scalar::all(0)); - + statePost.create(DP, 1, type); statePost.setTo(Scalar::all(0)); @@ -111,7 +111,7 @@ CV_EXPORTS const oclMat& KalmanFilter::predict(const oclMat& control) gemm(transitionMatrix, statePost, 1, oclMat(), 0, statePre); oclMat temp; - if(control.data) + if(control.data) gemm(controlMatrix, control, 1, statePre, 1, statePre); gemm(transitionMatrix, errorCovPost, 1, oclMat(), 0, temp1); gemm(temp1, transitionMatrix, 1, processNoiseCov, 1, errorCovPre, GEMM_2_T); diff --git a/modules/ocl/test/test_kalman.cpp b/modules/ocl/test/test_kalman.cpp index db3f19e872..13f9d0b81b 100644 --- a/modules/ocl/test/test_kalman.cpp +++ b/modules/ocl/test/test_kalman.cpp @@ -96,7 +96,7 @@ TEST_P(Kalman, Accuracy) kalman_filter_ocl.correct(Sample); //ocl end - //cpu start + //cpu start cv::KalmanFilter kalman_filter_cpu; kalman_filter_cpu.init(Dim, Dim); @@ -143,5 +143,4 @@ TEST_P(Kalman, Accuracy) } INSTANTIATE_TEST_CASE_P(OCL_Video, Kalman, Combine(Values(3, 7), Values(30))); -#endif // HAVE_OPENCL - +#endif // HAVE_OPENCL \ No newline at end of file From 7d8bde3330b714492cd6557f9c0b5123332f9b34 Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Mon, 9 Sep 2013 15:32:43 +0400 Subject: [PATCH 07/68] fixed ocl::merge in case of 2 and types CV_32S and CV_32F --- modules/ocl/src/opencl/merge_mat.cl | 4 +- modules/ocl/test/test_split_merge.cpp | 296 +++++++++----------------- 2 files changed, 99 insertions(+), 201 deletions(-) diff --git a/modules/ocl/src/opencl/merge_mat.cl b/modules/ocl/src/opencl/merge_mat.cl index ad3cebb95f..19e2340eb5 100644 --- a/modules/ocl/src/opencl/merge_mat.cl +++ b/modules/ocl/src/opencl/merge_mat.cl @@ -204,7 +204,7 @@ __kernel void merge_vector_C2_D4(__global int *mat_dst, int dst_step, int dst_ int src0 = *((__global int *)((__global uchar *)mat_src0 + src0_index + (x << 2))); int src1 = *((__global int *)((__global uchar *)mat_src1 + src1_index + (x << 2))); - *((__global int2 *)((__global uchar *)mat_dst + dst_index + (x << 4))) = (int2)(src0, src1); + *((__global int2 *)((__global uchar *)mat_dst + dst_index + (x << 3))) = (int2)(src0, src1); } } __kernel void merge_vector_C2_D5(__global float *mat_dst, int dst_step, int dst_offset, @@ -224,7 +224,7 @@ __kernel void merge_vector_C2_D5(__global float *mat_dst, int dst_step, int ds float src0 = *((__global float *)((__global uchar *)mat_src0 + src0_index + (x << 2))); float src1 = *((__global float *)((__global uchar *)mat_src1 + src1_index + (x << 2))); - *((__global float2 *)((__global uchar *)mat_dst + dst_index + (x << 4))) = (float2)(src0, src1); + *((__global float2 *)((__global uchar *)mat_dst + dst_index + (x << 3))) = (float2)(src0, src1); } } diff --git a/modules/ocl/test/test_split_merge.cpp b/modules/ocl/test/test_split_merge.cpp index 9663f5321c..52db49b028 100644 --- a/modules/ocl/test/test_split_merge.cpp +++ b/modules/ocl/test/test_split_merge.cpp @@ -52,39 +52,27 @@ using namespace cvtest; using namespace testing; using namespace std; -PARAM_TEST_CASE(MergeTestBase, MatType, int) +#define MAX_CHANNELS 4 + +PARAM_TEST_CASE(MergeTestBase, MatType, int, bool) { int type; int channels; + bool use_roi; //src mat - cv::Mat mat1; - cv::Mat mat2; - cv::Mat mat3; - cv::Mat mat4; - + cv::Mat mat[MAX_CHANNELS]; //dst mat cv::Mat dst; // set up roi - int roicols; - int roirows; - int src1x; - int src1y; - int src2x; - int src2y; - int src3x; - int src3y; - int src4x; - int src4y; - int dstx; - int dsty; + int roicols, roirows; + int srcx[MAX_CHANNELS]; + int srcy[MAX_CHANNELS]; + int dstx, dsty; //src mat with roi - cv::Mat mat1_roi; - cv::Mat mat2_roi; - cv::Mat mat3_roi; - cv::Mat mat4_roi; + cv::Mat mat_roi[MAX_CHANNELS]; //dst mat with roi cv::Mat dst_roi; @@ -93,78 +81,62 @@ PARAM_TEST_CASE(MergeTestBase, MatType, int) cv::ocl::oclMat gdst_whole; //ocl mat with roi - cv::ocl::oclMat gmat1; - cv::ocl::oclMat gmat2; - cv::ocl::oclMat gmat3; - cv::ocl::oclMat gmat4; + cv::ocl::oclMat gmat[MAX_CHANNELS]; cv::ocl::oclMat gdst; virtual void SetUp() { type = GET_PARAM(0); channels = GET_PARAM(1); + use_roi = GET_PARAM(2); cv::RNG &rng = TS::ptr()->get_rng(); cv::Size size(MWIDTH, MHEIGHT); - mat1 = randomMat(rng, size, CV_MAKETYPE(type, 1), 5, 16, false); - mat2 = randomMat(rng, size, CV_MAKETYPE(type, 1), 5, 16, false); - mat3 = randomMat(rng, size, CV_MAKETYPE(type, 1), 5, 16, false); - mat4 = randomMat(rng, size, CV_MAKETYPE(type, 1), 5, 16, false); - dst = randomMat(rng, size, CV_MAKETYPE(type, channels), 5, 16, false); - + for (int i = 0; i < channels; ++i) + mat[i] = randomMat(rng, size, CV_MAKETYPE(type, 1), 5, 16, false); + dst = randomMat(rng, size, CV_MAKETYPE(type, channels), 5, 16, false); } void random_roi() { -#ifdef RANDOMROI - //randomize ROI - cv::RNG &rng = TS::ptr()->get_rng(); - roicols = rng.uniform(1, mat1.cols); - roirows = rng.uniform(1, mat1.rows); - src1x = rng.uniform(0, mat1.cols - roicols); - src1y = rng.uniform(0, mat1.rows - roirows); - src2x = rng.uniform(0, mat2.cols - roicols); - src2y = rng.uniform(0, mat2.rows - roirows); - src3x = rng.uniform(0, mat3.cols - roicols); - src3y = rng.uniform(0, mat3.rows - roirows); - src4x = rng.uniform(0, mat4.cols - roicols); - src4y = rng.uniform(0, mat4.rows - roirows); - dstx = rng.uniform(0, dst.cols - roicols); - dsty = rng.uniform(0, dst.rows - roirows); -#else - roicols = mat1.cols; - roirows = mat1.rows; - src1x = 0; - src1y = 0; - src2x = 0; - src2y = 0; - src3x = 0; - src3y = 0; - src4x = 0; - src4y = 0; - dstx = 0; - dsty = 0; -#endif - - - mat1_roi = mat1(Rect(src1x, src1y, roicols, roirows)); - mat2_roi = mat2(Rect(src2x, src2y, roicols, roirows)); - mat3_roi = mat3(Rect(src3x, src3y, roicols, roirows)); - mat4_roi = mat4(Rect(src4x, src4y, roicols, roirows)); - + if (use_roi) + { + //randomize ROI + cv::RNG &rng = TS::ptr()->get_rng(); + roicols = rng.uniform(1, mat[0].cols); + roirows = rng.uniform(1, mat[0].rows); + + for (int i = 0; i < channels; ++i) + { + srcx[i] = rng.uniform(0, mat[i].cols - roicols); + srcy[i] = rng.uniform(0, mat[i].rows - roirows); + } + + dstx = rng.uniform(0, dst.cols - roicols); + dsty = rng.uniform(0, dst.rows - roirows); + } + else + { + roicols = mat[0].cols; + roirows = mat[0].rows; + for (int i = 0; i < channels; ++i) + srcx[i] = srcy[i] = 0; + + dstx = dsty = 0; + } + + for (int i = 0; i < channels; ++i) + mat_roi[i] = mat[i](Rect(srcx[i], srcy[i], roicols, roirows)); dst_roi = dst(Rect(dstx, dsty, roicols, roirows)); gdst_whole = dst; gdst = gdst_whole(Rect(dstx, dsty, roicols, roirows)); - gmat1 = mat1_roi; - gmat2 = mat2_roi; - gmat3 = mat3_roi; - gmat4 = mat4_roi; + for (int i = 0; i < channels; ++i) + gmat[i] = mat_roi[i]; } - }; struct Merge : MergeTestBase {}; @@ -175,159 +147,97 @@ TEST_P(Merge, Accuracy) { random_roi(); - std::vector dev_src; - dev_src.push_back(mat1_roi); - - if(channels >= 2) - dev_src.push_back(mat2_roi); - - if(channels >= 3) - dev_src.push_back(mat3_roi); - - if(channels >= 4) - dev_src.push_back(mat4_roi); - - std::vector dev_gsrc; - dev_gsrc.push_back(gmat1); - - if(channels >= 2) - dev_gsrc.push_back(gmat2); - - if(channels >= 3) - dev_gsrc.push_back(gmat3); - - if(channels >= 4) - dev_gsrc.push_back(gmat4); - - cv::merge(dev_src, dst_roi); - cv::ocl::merge(dev_gsrc, gdst); + cv::merge(mat_roi, channels, dst_roi); + cv::ocl::merge(gmat, channels, gdst); EXPECT_MAT_NEAR(dst, Mat(gdst_whole), 0.0); } } - - -PARAM_TEST_CASE(SplitTestBase, MatType, int) +PARAM_TEST_CASE(SplitTestBase, MatType, int, bool) { int type; int channels; + bool use_roi; //src mat cv::Mat mat; //dstmat - cv::Mat dst1; - cv::Mat dst2; - cv::Mat dst3; - cv::Mat dst4; + cv::Mat dst[MAX_CHANNELS]; // set up roi - int roicols; - int roirows; - int srcx; - int srcy; - int dst1x; - int dst1y; - int dst2x; - int dst2y; - int dst3x; - int dst3y; - int dst4x; - int dst4y; + int roicols, roirows; + int srcx, srcy; + int dstx[MAX_CHANNELS]; + int dsty[MAX_CHANNELS]; //src mat with roi cv::Mat mat_roi; //dst mat with roi - cv::Mat dst1_roi; - cv::Mat dst2_roi; - cv::Mat dst3_roi; - cv::Mat dst4_roi; + cv::Mat dst_roi[MAX_CHANNELS]; //ocl dst mat for testing - cv::ocl::oclMat gdst1_whole; - cv::ocl::oclMat gdst2_whole; - cv::ocl::oclMat gdst3_whole; - cv::ocl::oclMat gdst4_whole; + cv::ocl::oclMat gdst_whole[MAX_CHANNELS]; //ocl mat with roi cv::ocl::oclMat gmat; - cv::ocl::oclMat gdst1; - cv::ocl::oclMat gdst2; - cv::ocl::oclMat gdst3; - cv::ocl::oclMat gdst4; + cv::ocl::oclMat gdst[MAX_CHANNELS]; virtual void SetUp() { type = GET_PARAM(0); channels = GET_PARAM(1); + use_roi = GET_PARAM(2); cv::RNG &rng = TS::ptr()->get_rng(); cv::Size size(MWIDTH, MHEIGHT); mat = randomMat(rng, size, CV_MAKETYPE(type, channels), 5, 16, false); - dst1 = randomMat(rng, size, CV_MAKETYPE(type, 1), 5, 16, false); - dst2 = randomMat(rng, size, CV_MAKETYPE(type, 1), 5, 16, false); - dst3 = randomMat(rng, size, CV_MAKETYPE(type, 1), 5, 16, false); - dst4 = randomMat(rng, size, CV_MAKETYPE(type, 1), 5, 16, false); - - } + for (int i = 0; i < channels; ++i) + dst[i] = randomMat(rng, size, CV_MAKETYPE(type, 1), 5, 16, false); } void random_roi() { -#ifdef RANDOMROI - //randomize ROI - cv::RNG &rng = TS::ptr()->get_rng(); - roicols = rng.uniform(1, mat.cols); - roirows = rng.uniform(1, mat.rows); - srcx = rng.uniform(0, mat.cols - roicols); - srcy = rng.uniform(0, mat.rows - roirows); - dst1x = rng.uniform(0, dst1.cols - roicols); - dst1y = rng.uniform(0, dst1.rows - roirows); - dst2x = rng.uniform(0, dst2.cols - roicols); - dst2y = rng.uniform(0, dst2.rows - roirows); - dst3x = rng.uniform(0, dst3.cols - roicols); - dst3y = rng.uniform(0, dst3.rows - roirows); - dst4x = rng.uniform(0, dst4.cols - roicols); - dst4y = rng.uniform(0, dst4.rows - roirows); -#else - roicols = mat.cols; - roirows = mat.rows; - srcx = 0; - srcy = 0; - dst1x = 0; - dst1y = 0; - dst2x = 0; - dst2y = 0; - dst3x = 0; - dst3y = 0; - dst4x = 0; - dst4y = 0; -#endif + if (use_roi) + { + //randomize ROI + cv::RNG &rng = TS::ptr()->get_rng(); + roicols = rng.uniform(1, mat.cols); + roirows = rng.uniform(1, mat.rows); + srcx = rng.uniform(0, mat.cols - roicols); + srcy = rng.uniform(0, mat.rows - roirows); + + for (int i = 0; i < channels; ++i) + { + dstx[i] = rng.uniform(0, dst[i].cols - roicols); + dsty[i] = rng.uniform(0, dst[i].rows - roirows); + } + } + else + { + roicols = mat.cols; + roirows = mat.rows; + srcx = srcy = 0; + + for (int i = 0; i < channels; ++i) + dstx[i] = dsty[i] = 0; + } mat_roi = mat(Rect(srcx, srcy, roicols, roirows)); - dst1_roi = dst1(Rect(dst1x, dst1y, roicols, roirows)); - dst2_roi = dst2(Rect(dst2x, dst2y, roicols, roirows)); - dst3_roi = dst3(Rect(dst3x, dst3y, roicols, roirows)); - dst4_roi = dst4(Rect(dst4x, dst4y, roicols, roirows)); - - gdst1_whole = dst1; - gdst1 = gdst1_whole(Rect(dst1x, dst1y, roicols, roirows)); - - gdst2_whole = dst2; - gdst2 = gdst2_whole(Rect(dst2x, dst2y, roicols, roirows)); + for (int i = 0; i < channels; ++i) + dst_roi[i] = dst[i](Rect(dstx[i], dsty[i], roicols, roirows)); - gdst3_whole = dst3; - gdst3 = gdst3_whole(Rect(dst3x, dst3y, roicols, roirows)); - - gdst4_whole = dst4; - gdst4 = gdst4_whole(Rect(dst4x, dst4y, roicols, roirows)); + for (int i = 0; i < channels; ++i) + { + gdst_whole[i] = dst[i]; + gdst[i] = gdst_whole[i](Rect(dstx[i], dsty[i], roicols, roirows)); + } gmat = mat_roi; } - }; struct Split : SplitTestBase {}; @@ -338,33 +248,21 @@ TEST_P(Split, Accuracy) { random_roi(); - cv::Mat dev_dst[4] = {dst1_roi, dst2_roi, dst3_roi, dst4_roi}; - cv::ocl::oclMat dev_gdst[4] = {gdst1, gdst2, gdst3, gdst4}; - - cv::split(mat_roi, dev_dst); - cv::ocl::split(gmat, dev_gdst); - - if(channels >= 1) - EXPECT_MAT_NEAR(dst1, Mat(gdst1_whole), 0.0); - - if(channels >= 2) - EXPECT_MAT_NEAR(dst2, Mat(gdst2_whole), 0.0); - - if(channels >= 3) - EXPECT_MAT_NEAR(dst3, Mat(gdst3_whole), 0.0); + cv::split(mat_roi, dst_roi); + cv::ocl::split(gmat, gdst); - if(channels >= 4) - EXPECT_MAT_NEAR(dst4, Mat(gdst4_whole), 0.0); + for (int i = 0; i < channels; ++i) + EXPECT_MAT_NEAR(dst[i], Mat(gdst_whole[i]), 0.0); } } INSTANTIATE_TEST_CASE_P(SplitMerge, Merge, Combine( - Values(CV_8U, CV_32S, CV_32F), Values(1, 3, 4))); + Values(CV_8U, CV_8S, CV_16U, CV_16S, CV_32S, CV_32F), Range(1, 5), Bool())); INSTANTIATE_TEST_CASE_P(SplitMerge, Split , Combine( - Values(CV_8U, CV_32S, CV_32F), Values(1, 3, 4))); + Values(CV_8U, CV_8S, CV_16U, CV_16S, CV_32S, CV_32F), Range(1, 5), Bool())); #endif // HAVE_OPENCL From 61f21078297802bb46377ff3b32229a82f31389a Mon Sep 17 00:00:00 2001 From: Vadim Pisarevsky Date: Mon, 9 Sep 2013 16:13:39 +0400 Subject: [PATCH 08/68] added IPP optimization of separable 32f filters; fixed IPP version check in DFT; fixed conditions in IPP optimization of norm functions. --- modules/core/src/dxt.cpp | 2 +- modules/core/src/stat.cpp | 18 ++++++++---- modules/imgproc/src/filter.cpp | 52 +++++++++++++++++++++++++++++---- modules/imgproc/src/imgwarp.cpp | 8 ++--- 4 files changed, 63 insertions(+), 17 deletions(-) diff --git a/modules/core/src/dxt.cpp b/modules/core/src/dxt.cpp index a802868df0..e6fed4eae7 100644 --- a/modules/core/src/dxt.cpp +++ b/modules/core/src/dxt.cpp @@ -50,7 +50,7 @@ namespace cv # pragma warning(disable: 4748) #endif -#if defined HAVE_IPP && IPP_VERSION_MAJOR >= 7 +#if defined HAVE_IPP && IPP_VERSION_MAJOR*100 + IPP_VERSION_MINOR >= 701 #define USE_IPP_DFT 1 #else #undef USE_IPP_DFT diff --git a/modules/core/src/stat.cpp b/modules/core/src/stat.cpp index b3fa82c28e..ff84a3420a 100644 --- a/modules/core/src/stat.cpp +++ b/modules/core/src/stat.cpp @@ -1607,13 +1607,15 @@ double cv::norm( InputArray _src, int normType, InputArray _mask ) int depth = src.depth(), cn = src.channels(); normType &= 7; - CV_Assert( normType == NORM_INF || normType == NORM_L1 || normType == NORM_L2 || normType == NORM_L2SQR || + CV_Assert( normType == NORM_INF || normType == NORM_L1 || + normType == NORM_L2 || normType == NORM_L2SQR || ((normType == NORM_HAMMING || normType == NORM_HAMMING2) && src.type() == CV_8U) ); #if defined (HAVE_IPP) && (IPP_VERSION_MAJOR >= 7) size_t total_size = src.total(); int rows = src.size[0], cols = (int)(total_size/rows); - if( src.dims == 2 || (src.isContinuous() && mask.isContinuous() && cols > 0 && (size_t)rows*cols == total_size) + if( (src.dims == 2 || (src.isContinuous() && mask.isContinuous())) + && cols > 0 && (size_t)rows*cols == total_size && (normType == NORM_INF || normType == NORM_L1 || normType == NORM_L2 || normType == NORM_L2SQR) ) { IppiSize sz = { cols, rows }; @@ -1900,8 +1902,10 @@ double cv::norm( InputArray _src1, InputArray _src2, int normType, InputArray _m ((normType == NORM_HAMMING || normType == NORM_HAMMING2) && src1.type() == CV_8U) ); size_t total_size = src1.total(); int rows = src1.size[0], cols = (int)(total_size/rows); - if( src1.dims == 2 || (src1.isContinuous() && src2.isContinuous() && mask.isContinuous() && cols > 0 && (size_t)rows*cols == total_size) - && (normType == NORM_INF || normType == NORM_L1 || normType == NORM_L2 || normType == NORM_L2SQR) ) + if( (src1.dims == 2 || (src1.isContinuous() && src2.isContinuous() && mask.isContinuous())) + && cols > 0 && (size_t)rows*cols == total_size + && (normType == NORM_INF || normType == NORM_L1 || + normType == NORM_L2 || normType == NORM_L2SQR) ) { IppiSize sz = { cols, rows }; int type = src1.type(); @@ -1974,13 +1978,15 @@ double cv::norm( InputArray _src1, InputArray _src2, int normType, InputArray _m CV_Assert( src1.size == src2.size && src1.type() == src2.type() ); normType &= 7; - CV_Assert( normType == NORM_INF || normType == NORM_L1 || normType == NORM_L2 || normType == NORM_L2SQR || + CV_Assert( normType == NORM_INF || normType == NORM_L1 || + normType == NORM_L2 || normType == NORM_L2SQR || ((normType == NORM_HAMMING || normType == NORM_HAMMING2) && src1.type() == CV_8U) ); #if defined (HAVE_IPP) && (IPP_VERSION_MAJOR >= 7) size_t total_size = src1.total(); int rows = src1.size[0], cols = (int)(total_size/rows); - if( src1.dims == 2 || (src1.isContinuous() && src2.isContinuous() && mask.isContinuous() && cols > 0 && (size_t)rows*cols == total_size) + if( (src1.dims == 2 || (src1.isContinuous() && src2.isContinuous() && mask.isContinuous())) + && cols > 0 && (size_t)rows*cols == total_size && (normType == NORM_INF || normType == NORM_L1 || normType == NORM_L2 || normType == NORM_L2SQR) ) { IppiSize sz = { cols, rows }; diff --git a/modules/imgproc/src/filter.cpp b/modules/imgproc/src/filter.cpp index a2bfa6a4b5..1d05d3c2c3 100644 --- a/modules/imgproc/src/filter.cpp +++ b/modules/imgproc/src/filter.cpp @@ -46,6 +46,12 @@ Base Image Filter \****************************************************************************************/ +#if defined HAVE_IPP && IPP_VERSION_MAJOR*100 + IPP_VERSION_MINOR >= 701 +#define USE_IPP_SEP_FILTERS 1 +#else +#undef USE_IPP_SEP_FILTERS +#endif + /* Various border types, image boundaries are denoted with '|' @@ -1445,21 +1451,53 @@ struct RowVec_32f RowVec_32f( const Mat& _kernel ) { kernel = _kernel; + haveSSE = checkHardwareSupport(CV_CPU_SSE); +#ifdef USE_IPP_SEP_FILTERS + bufsz = -1; +#endif } int operator()(const uchar* _src, uchar* _dst, int width, int cn) const { - if( !checkHardwareSupport(CV_CPU_SSE) ) - return 0; - - int i = 0, k, _ksize = kernel.rows + kernel.cols - 1; + int _ksize = kernel.rows + kernel.cols - 1; + const float* src0 = (const float*)_src; float* dst = (float*)_dst; const float* _kx = (const float*)kernel.data; + +#ifdef USE_IPP_SEP_FILTERS + IppiSize roisz = { width, 1 }; + if( (cn == 1 || cn == 3) && width >= _ksize*8 ) + { + if( bufsz < 0 ) + { + if( (cn == 1 && ippiFilterRowBorderPipelineGetBufferSize_32f_C1R(roisz, _ksize, &bufsz) < 0) || + (cn == 3 && ippiFilterRowBorderPipelineGetBufferSize_32f_C3R(roisz, _ksize, &bufsz) < 0)) + return 0; + } + AutoBuffer buf(bufsz + 64); + uchar* bufptr = alignPtr((uchar*)buf, 32); + int step = (int)(width*sizeof(dst[0])*cn); + float borderValue[] = {0.f, 0.f, 0.f}; + // here is the trick. IPP needs border type and extrapolates the row. We did it already. + // So we pass anchor=0 and ignore the right tail of results since they are incorrect there. + if( (cn == 1 && ippiFilterRowBorderPipeline_32f_C1R(src0, step, &dst, roisz, _kx, _ksize, 0, + ippBorderRepl, borderValue[0], bufptr) < 0) || + (cn == 3 && ippiFilterRowBorderPipeline_32f_C3R(src0, step, &dst, roisz, _kx, _ksize, 0, + ippBorderRepl, borderValue, bufptr) < 0)) + return 0; + return width - _ksize + 1; + } +#endif + + if( !haveSSE ) + return 0; + + int i = 0, k; width *= cn; for( ; i <= width - 8; i += 8 ) { - const float* src = (const float*)_src + i; + const float* src = src0 + i; __m128 f, s0 = _mm_setzero_ps(), s1 = s0, x0, x1; for( k = 0; k < _ksize; k++, src += cn ) { @@ -1478,6 +1516,10 @@ struct RowVec_32f } Mat kernel; + bool haveSSE; +#ifdef USE_IPP_SEP_FILTERS + mutable int bufsz; +#endif }; diff --git a/modules/imgproc/src/imgwarp.cpp b/modules/imgproc/src/imgwarp.cpp index a4fda282dc..3bbfe69ac0 100644 --- a/modules/imgproc/src/imgwarp.cpp +++ b/modules/imgproc/src/imgwarp.cpp @@ -1689,12 +1689,10 @@ public: IppiRect dstroi = { 0, dsty, dstwidth, dstheight - dsty }; int bufsize; ippiResizeGetBufSize( srcroi, dstroi, cn, mode, &bufsize ); - Ipp8u *buf; - buf = ippsMalloc_8u( bufsize ); - IppStatus sts; - if( func( src.data, ippiSize(src.cols, src.rows), (int)src.step[0], srcroi, dst.data, (int)dst.step[0], dstroi, inv_scale_x, inv_scale_y, 0, 0, mode, buf ) < 0 ) + AutoBuffer buf(bufsize + 64); + uchar* bufptr = alignPtr((uchar*)buf, 32); + if( func( src.data, ippiSize(src.cols, src.rows), (int)src.step[0], srcroi, dst.data, (int)dst.step[0], dstroi, inv_scale_x, inv_scale_y, 0, 0, mode, bufptr ) < 0 ) *ok = false; - ippsFree(buf); } private: Mat &src; From b07b9aca5aec2b8cc61d5724d85f6c28a8f02437 Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Mon, 9 Sep 2013 17:16:47 +0400 Subject: [PATCH 09/68] fixed HOG perf test --- modules/ocl/perf/perf_hog.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/modules/ocl/perf/perf_hog.cpp b/modules/ocl/perf/perf_hog.cpp index 15846d8315..fe5d9d1904 100644 --- a/modules/ocl/perf/perf_hog.cpp +++ b/modules/ocl/perf/perf_hog.cpp @@ -49,6 +49,23 @@ using namespace perf; ///////////// HOG//////////////////////// +struct RectLess : + public std::binary_function +{ + bool operator()(const cv::Rect& a, + const cv::Rect& b) const + { + if (a.x != b.x) + return a.x < b.x; + else if (a.y != b.y) + return a.y < b.y; + else if (a.width != b.width) + return a.width < b.width; + else + return a.height < b.height; + } +}; + PERF_TEST(HOGFixture, HOG) { Mat src = imread(getDataPath("gpu/hog/road.png"), cv::IMREAD_GRAYSCALE); @@ -64,6 +81,7 @@ PERF_TEST(HOGFixture, HOG) TEST_CYCLE() hog.detectMultiScale(src, found_locations); + std::sort(found_locations.begin(), found_locations.end(), RectLess()); SANITY_CHECK(found_locations, 1 + DBL_EPSILON); } else if (RUN_OCL_IMPL) @@ -74,6 +92,7 @@ PERF_TEST(HOGFixture, HOG) OCL_TEST_CYCLE() ocl_hog.detectMultiScale(oclSrc, found_locations); + std::sort(found_locations.begin(), found_locations.end(), RectLess()); SANITY_CHECK(found_locations, 1 + DBL_EPSILON); } else From 16357418d5e801aa9014fdf4bd70bee04a0880be Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Mon, 9 Sep 2013 18:04:52 +0400 Subject: [PATCH 10/68] increased time limit for bilateral filter performance tests --- modules/ocl/perf/perf_filters.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/modules/ocl/perf/perf_filters.cpp b/modules/ocl/perf/perf_filters.cpp index aa562412bc..7f2758877b 100644 --- a/modules/ocl/perf/perf_filters.cpp +++ b/modules/ocl/perf/perf_filters.cpp @@ -333,13 +333,13 @@ PERF_TEST_P(BilateralFixture, Bilateral, const Size_MatType_t params = GetParam(); const Size srcSize = get<0>(params); const int type = get<1>(params), d = 7; - double sigmacolor = 50.0, sigmaspace = 50.0; + const double sigmacolor = 50.0, sigmaspace = 50.0; Mat src(srcSize, type), dst(srcSize, type); declare.in(src, WARMUP_RNG).out(dst); - if (srcSize == OCL_SIZE_4000 && type == CV_8UC3) - declare.time(8); + if (srcSize == OCL_SIZE_4000) + declare.time(type == CV_8UC3 ? 8 : 4.5); if (RUN_OCL_IMPL) { @@ -372,14 +372,16 @@ PERF_TEST_P(adaptiveBilateralFixture, adaptiveBilateral, const Size_MatType_t params = GetParam(); const Size srcSize = get<0>(params); const int type = get<1>(params); - double sigmaspace = 10.0; - Size ksize(9,9); + const double sigmaspace = 10.0; + Size ksize(9, 9); Mat src(srcSize, type), dst(srcSize, type); declare.in(src, WARMUP_RNG).out(dst); if (srcSize == OCL_SIZE_4000) - declare.time(15); + declare.time(type == CV_8UC3 ? 46 : 28); + else if (srcSize == OCL_SIZE_2000) + declare.time(type == CV_8UC3 ? 11 : 7); if (RUN_OCL_IMPL) { @@ -389,7 +391,7 @@ PERF_TEST_P(adaptiveBilateralFixture, adaptiveBilateral, oclDst.download(dst); - SANITY_CHECK(dst, 1.); + SANITY_CHECK(dst, 1.0); } else if (RUN_PLAIN_IMPL) { From 6b9c451938f9f65f3ea2e882736a0717db312703 Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Tue, 10 Sep 2013 11:53:34 +0400 Subject: [PATCH 11/68] added CV_16UC(1, 3, 4), CV_16SC(1, 3, 4) data types support in ocl::pyrUp and ocl::pyrDown --- modules/ocl/src/opencl/pyr_down.cl | 615 +++++++++++++++++++++++++---- modules/ocl/src/opencl/pyr_up.cl | 259 ++++++++++-- modules/ocl/src/pyrdown.cpp | 17 +- modules/ocl/src/pyrup.cpp | 5 + modules/ocl/test/test_pyramids.cpp | 33 +- 5 files changed, 791 insertions(+), 138 deletions(-) diff --git a/modules/ocl/src/opencl/pyr_down.cl b/modules/ocl/src/opencl/pyr_down.cl index 9fe8e8a974..e40ad3492d 100644 --- a/modules/ocl/src/opencl/pyr_down.cl +++ b/modules/ocl/src/opencl/pyr_down.cl @@ -43,37 +43,6 @@ // //M*/ -//#pragma OPENCL EXTENSION cl_amd_printf : enable - - -uchar round_uchar_int(int v) -{ - return (uchar)((uint)v <= 255 ? v : v > 0 ? 255 : 0); -} - -uchar round_uchar_float(float v) -{ - return round_uchar_int(convert_int_sat_rte(v)); -} - -uchar4 round_uchar4_int4(int4 v) -{ - uchar4 result; - result.x = (uchar)(v.x <= 255 ? v.x : v.x > 0 ? 255 : 0); - result.y = (uchar)(v.y <= 255 ? v.y : v.y > 0 ? 255 : 0); - result.z = (uchar)(v.z <= 255 ? v.z : v.z > 0 ? 255 : 0); - result.w = (uchar)(v.w <= 255 ? v.w : v.w > 0 ? 255 : 0); - return result; -} - -uchar4 round_uchar4_float4(float4 v) -{ - return round_uchar4_int4(convert_int4_sat_rte(v)); -} - - - - int idx_row_low(int y, int last_row) { return abs(y) % (last_row + 1); @@ -104,6 +73,10 @@ int idx_col(int x, int last_col) return idx_col_low(idx_col_high(x, last_col), last_col); } +/////////////////////////////////////////////////////////////////////// +////////////////////////// CV_8UC1 /////////////////////////////////// +/////////////////////////////////////////////////////////////////////// + __kernel void pyrDown_C1_D0(__global uchar * srcData, int srcStep, int srcRows, int srcCols, __global uchar *dst, int dstStep, int dstCols) { const int x = get_global_id(0); @@ -211,10 +184,14 @@ __kernel void pyrDown_C1_D0(__global uchar * srcData, int srcStep, int srcRows, const int dst_x = (get_group_id(0) * get_local_size(0) + tid2) / 2; if (dst_x < dstCols) - dst[y * dstStep + dst_x] = round_uchar_float(sum); + dst[y * dstStep + dst_x] = convert_uchar_sat_rte(sum); } } +/////////////////////////////////////////////////////////////////////// +////////////////////////// CV_8UC4 /////////////////////////////////// +/////////////////////////////////////////////////////////////////////// + __kernel void pyrDown_C4_D0(__global uchar4 * srcData, int srcStep, int srcRows, int srcCols, __global uchar4 *dst, int dstStep, int dstCols) { const int x = get_global_id(0); @@ -228,16 +205,16 @@ __kernel void pyrDown_C4_D0(__global uchar4 * srcData, int srcStep, int srcRows, const int last_row = srcRows - 1; const int last_col = srcCols - 1; - float4 co1 = 0.375f;//(float4)(0.375f, 0.375f, 0.375f, 0.375f); - float4 co2 = 0.25f;//(float4)(0.25f, 0.25f, 0.25f, 0.25f); - float4 co3 = 0.0625f;//(float4)(0.0625f, 0.0625f, 0.0625f, 0.0625f); + float4 co1 = 0.375f; + float4 co2 = 0.25f; + float4 co3 = 0.0625f; if (src_y >= 2 && src_y < srcRows - 2 && x >= 2 && x < srcCols - 2) { sum = co3 * convert_float4((((srcData + (src_y - 2) * srcStep / 4))[x])); - sum = sum + co2 * convert_float4((((srcData + (src_y - 1) * srcStep / 4))[x])); - sum = sum + co1 * convert_float4((((srcData + (src_y ) * srcStep / 4))[x])); - sum = sum + co2 * convert_float4((((srcData + (src_y + 1) * srcStep / 4))[x])); + sum = sum + co2 * convert_float4((((srcData + (src_y - 1) * srcStep / 4))[x])); + sum = sum + co1 * convert_float4((((srcData + (src_y ) * srcStep / 4))[x])); + sum = sum + co2 * convert_float4((((srcData + (src_y + 1) * srcStep / 4))[x])); sum = sum + co3 * convert_float4((((srcData + (src_y + 2) * srcStep / 4))[x])); smem[2 + get_local_id(0)] = sum; @@ -247,9 +224,9 @@ __kernel void pyrDown_C4_D0(__global uchar4 * srcData, int srcStep, int srcRows, const int left_x = x - 2; sum = co3 * convert_float4((((srcData + (src_y - 2) * srcStep / 4))[left_x])); - sum = sum + co2 * convert_float4((((srcData + (src_y - 1) * srcStep / 4))[left_x])); - sum = sum + co1 * convert_float4((((srcData + (src_y ) * srcStep / 4))[left_x])); - sum = sum + co2 * convert_float4((((srcData + (src_y + 1) * srcStep / 4))[left_x])); + sum = sum + co2 * convert_float4((((srcData + (src_y - 1) * srcStep / 4))[left_x])); + sum = sum + co1 * convert_float4((((srcData + (src_y ) * srcStep / 4))[left_x])); + sum = sum + co2 * convert_float4((((srcData + (src_y + 1) * srcStep / 4))[left_x])); sum = sum + co3 * convert_float4((((srcData + (src_y + 2) * srcStep / 4))[left_x])); smem[get_local_id(0)] = sum; @@ -260,9 +237,9 @@ __kernel void pyrDown_C4_D0(__global uchar4 * srcData, int srcStep, int srcRows, const int right_x = x + 2; sum = co3 * convert_float4((((srcData + (src_y - 2) * srcStep / 4))[right_x])); - sum = sum + co2 * convert_float4((((srcData + (src_y - 1) * srcStep / 4))[right_x])); - sum = sum + co1 * convert_float4((((srcData + (src_y ) * srcStep / 4))[right_x])); - sum = sum + co2 * convert_float4((((srcData + (src_y + 1) * srcStep / 4))[right_x])); + sum = sum + co2 * convert_float4((((srcData + (src_y - 1) * srcStep / 4))[right_x])); + sum = sum + co1 * convert_float4((((srcData + (src_y ) * srcStep / 4))[right_x])); + sum = sum + co2 * convert_float4((((srcData + (src_y + 1) * srcStep / 4))[right_x])); sum = sum + co3 * convert_float4((((srcData + (src_y + 2) * srcStep / 4))[right_x])); smem[4 + get_local_id(0)] = sum; @@ -273,9 +250,9 @@ __kernel void pyrDown_C4_D0(__global uchar4 * srcData, int srcStep, int srcRows, int col = idx_col(x, last_col); sum = co3 * convert_float4((((srcData + idx_row(src_y - 2, last_row) * srcStep / 4))[col])); - sum = sum + co2 * convert_float4((((srcData + idx_row(src_y - 1, last_row) * srcStep / 4))[col])); - sum = sum + co1 * convert_float4((((srcData + idx_row(src_y , last_row) * srcStep / 4))[col])); - sum = sum + co2 * convert_float4((((srcData + idx_row(src_y + 1, last_row) * srcStep / 4))[col])); + sum = sum + co2 * convert_float4((((srcData + idx_row(src_y - 1, last_row) * srcStep / 4))[col])); + sum = sum + co1 * convert_float4((((srcData + idx_row(src_y , last_row) * srcStep / 4))[col])); + sum = sum + co2 * convert_float4((((srcData + idx_row(src_y + 1, last_row) * srcStep / 4))[col])); sum = sum + co3 * convert_float4((((srcData + idx_row(src_y + 2, last_row) * srcStep / 4))[col])); smem[2 + get_local_id(0)] = sum; @@ -287,9 +264,9 @@ __kernel void pyrDown_C4_D0(__global uchar4 * srcData, int srcStep, int srcRows, col = idx_col(left_x, last_col); sum = co3 * convert_float4((((srcData + idx_row(src_y - 2, last_row) * srcStep / 4))[col])); - sum = sum + co2 * convert_float4((((srcData + idx_row(src_y - 1, last_row) * srcStep / 4))[col])); - sum = sum + co1 * convert_float4((((srcData + idx_row(src_y , last_row) * srcStep / 4))[col])); - sum = sum + co2 * convert_float4((((srcData + idx_row(src_y + 1, last_row) * srcStep / 4))[col])); + sum = sum + co2 * convert_float4((((srcData + idx_row(src_y - 1, last_row) * srcStep / 4))[col])); + sum = sum + co1 * convert_float4((((srcData + idx_row(src_y , last_row) * srcStep / 4))[col])); + sum = sum + co2 * convert_float4((((srcData + idx_row(src_y + 1, last_row) * srcStep / 4))[col])); sum = sum + co3 * convert_float4((((srcData + idx_row(src_y + 2, last_row) * srcStep / 4))[col])); smem[get_local_id(0)] = sum; @@ -302,9 +279,9 @@ __kernel void pyrDown_C4_D0(__global uchar4 * srcData, int srcStep, int srcRows, col = idx_col(right_x, last_col); sum = co3 * convert_float4((((srcData + idx_row(src_y - 2, last_row) * srcStep / 4))[col])); - sum = sum + co2 * convert_float4((((srcData + idx_row(src_y - 1, last_row) * srcStep / 4))[col])); - sum = sum + co1 * convert_float4((((srcData + idx_row(src_y , last_row) * srcStep / 4))[col])); - sum = sum + co2 * convert_float4((((srcData + idx_row(src_y + 1, last_row) * srcStep / 4))[col])); + sum = sum + co2 * convert_float4((((srcData + idx_row(src_y - 1, last_row) * srcStep / 4))[col])); + sum = sum + co1 * convert_float4((((srcData + idx_row(src_y , last_row) * srcStep / 4))[col])); + sum = sum + co2 * convert_float4((((srcData + idx_row(src_y + 1, last_row) * srcStep / 4))[col])); sum = sum + co3 * convert_float4((((srcData + idx_row(src_y + 2, last_row) * srcStep / 4))[col])); smem[4 + get_local_id(0)] = sum; @@ -318,18 +295,490 @@ __kernel void pyrDown_C4_D0(__global uchar4 * srcData, int srcStep, int srcRows, const int tid2 = get_local_id(0) * 2; sum = co3 * smem[2 + tid2 - 2]; - sum = sum + co2 * smem[2 + tid2 - 1]; - sum = sum + co1 * smem[2 + tid2 ]; - sum = sum + co2 * smem[2 + tid2 + 1]; + sum = sum + co2 * smem[2 + tid2 - 1]; + sum = sum + co1 * smem[2 + tid2 ]; + sum = sum + co2 * smem[2 + tid2 + 1]; sum = sum + co3 * smem[2 + tid2 + 2]; const int dst_x = (get_group_id(0) * get_local_size(0) + tid2) / 2; if (dst_x < dstCols) - dst[y * dstStep / 4 + dst_x] = round_uchar4_float4(sum); + dst[y * dstStep / 4 + dst_x] = convert_uchar4_sat_rte(sum); } } +/////////////////////////////////////////////////////////////////////// +////////////////////////// CV_16UC1 ////////////////////////////////// +/////////////////////////////////////////////////////////////////////// + +__kernel void pyrDown_C1_D2(__global ushort * srcData, int srcStep, int srcRows, int srcCols, __global ushort *dst, int dstStep, int dstCols) +{ + const int x = get_global_id(0); + const int y = get_group_id(1); + + __local float smem[256 + 4]; + + float sum; + + const int src_y = 2*y; + const int last_row = srcRows - 1; + const int last_col = srcCols - 1; + + if (src_y >= 2 && src_y < srcRows - 2 && x >= 2 && x < srcCols - 2) + { + sum = 0.0625f * ((__global ushort*)((__global char*)srcData + (src_y - 2) * srcStep))[x]; + sum = sum + 0.25f * ((__global ushort*)((__global char*)srcData + (src_y - 1) * srcStep))[x]; + sum = sum + 0.375f * ((__global ushort*)((__global char*)srcData + (src_y ) * srcStep))[x]; + sum = sum + 0.25f * ((__global ushort*)((__global char*)srcData + (src_y + 1) * srcStep))[x]; + sum = sum + 0.0625f * ((__global ushort*)((__global char*)srcData + (src_y + 2) * srcStep))[x]; + + smem[2 + get_local_id(0)] = sum; + + if (get_local_id(0) < 2) + { + const int left_x = x - 2; + + sum = 0.0625f * ((__global ushort*)((__global char*)srcData + (src_y - 2) * srcStep))[left_x]; + sum = sum + 0.25f * ((__global ushort*)((__global char*)srcData + (src_y - 1) * srcStep))[left_x]; + sum = sum + 0.375f * ((__global ushort*)((__global char*)srcData + (src_y ) * srcStep))[left_x]; + sum = sum + 0.25f * ((__global ushort*)((__global char*)srcData + (src_y + 1) * srcStep))[left_x]; + sum = sum + 0.0625f * ((__global ushort*)((__global char*)srcData + (src_y + 2) * srcStep))[left_x]; + + smem[get_local_id(0)] = sum; + } + + if (get_local_id(0) > 253) + { + const int right_x = x + 2; + + sum = 0.0625f * ((__global ushort*)((__global char*)srcData + (src_y - 2) * srcStep))[right_x]; + sum = sum + 0.25f * ((__global ushort*)((__global char*)srcData + (src_y - 1) * srcStep))[right_x]; + sum = sum + 0.375f * ((__global ushort*)((__global char*)srcData + (src_y ) * srcStep))[right_x]; + sum = sum + 0.25f * ((__global ushort*)((__global char*)srcData + (src_y + 1) * srcStep))[right_x]; + sum = sum + 0.0625f * ((__global ushort*)((__global char*)srcData + (src_y + 2) * srcStep))[right_x]; + + smem[4 + get_local_id(0)] = sum; + } + } + else + { + int col = idx_col(x, last_col); + + sum = 0.0625f * ((__global ushort*)((__global char*)srcData + idx_row(src_y - 2, last_row) * srcStep))[col]; + sum = sum + 0.25f * ((__global ushort*)((__global char*)srcData + idx_row(src_y - 1, last_row) * srcStep))[col]; + sum = sum + 0.375f * ((__global ushort*)((__global char*)srcData + idx_row(src_y , last_row) * srcStep))[col]; + sum = sum + 0.25f * ((__global ushort*)((__global char*)srcData + idx_row(src_y + 1, last_row) * srcStep))[col]; + sum = sum + 0.0625f * ((__global ushort*)((__global char*)srcData + idx_row(src_y + 2, last_row) * srcStep))[col]; + + smem[2 + get_local_id(0)] = sum; + + if (get_local_id(0) < 2) + { + const int left_x = x - 2; + + col = idx_col(left_x, last_col); + + sum = 0.0625f * ((__global ushort*)((__global char*)srcData + idx_row(src_y - 2, last_row) * srcStep))[col]; + sum = sum + 0.25f * ((__global ushort*)((__global char*)srcData + idx_row(src_y - 1, last_row) * srcStep))[col]; + sum = sum + 0.375f * ((__global ushort*)((__global char*)srcData + idx_row(src_y , last_row) * srcStep))[col]; + sum = sum + 0.25f * ((__global ushort*)((__global char*)srcData + idx_row(src_y + 1, last_row) * srcStep))[col]; + sum = sum + 0.0625f * ((__global ushort*)((__global char*)srcData + idx_row(src_y + 2, last_row) * srcStep))[col]; + + smem[get_local_id(0)] = sum; + } + + if (get_local_id(0) > 253) + { + const int right_x = x + 2; + + col = idx_col(right_x, last_col); + + sum = 0.0625f * ((__global ushort*)((__global char*)srcData + idx_row(src_y - 2, last_row) * srcStep))[col]; + sum = sum + 0.25f * ((__global ushort*)((__global char*)srcData + idx_row(src_y - 1, last_row) * srcStep))[col]; + sum = sum + 0.375f * ((__global ushort*)((__global char*)srcData + idx_row(src_y , last_row) * srcStep))[col]; + sum = sum + 0.25f * ((__global ushort*)((__global char*)srcData + idx_row(src_y + 1, last_row) * srcStep))[col]; + sum = sum + 0.0625f * ((__global ushort*)((__global char*)srcData + idx_row(src_y + 2, last_row) * srcStep))[col]; + + smem[4 + get_local_id(0)] = sum; + } + } + + barrier(CLK_LOCAL_MEM_FENCE); + + if (get_local_id(0) < 128) + { + const int tid2 = get_local_id(0) * 2; + + sum = 0.0625f * smem[2 + tid2 - 2]; + sum = sum + 0.25f * smem[2 + tid2 - 1]; + sum = sum + 0.375f * smem[2 + tid2 ]; + sum = sum + 0.25f * smem[2 + tid2 + 1]; + sum = sum + 0.0625f * smem[2 + tid2 + 2]; + + const int dst_x = (get_group_id(0) * get_local_size(0) + tid2) / 2; + + if (dst_x < dstCols) + dst[y * dstStep / 2 + dst_x] = convert_ushort_sat_rte(sum); + } +} + +/////////////////////////////////////////////////////////////////////// +////////////////////////// CV_16UC4 ////////////////////////////////// +/////////////////////////////////////////////////////////////////////// + +__kernel void pyrDown_C4_D2(__global ushort4 * srcData, int srcStep, int srcRows, int srcCols, __global ushort4 *dst, int dstStep, int dstCols) +{ + const int x = get_global_id(0); + const int y = get_group_id(1); + + __local float4 smem[256 + 4]; + + float4 sum; + + const int src_y = 2*y; + const int last_row = srcRows - 1; + const int last_col = srcCols - 1; + + float4 co1 = 0.375f; + float4 co2 = 0.25f; + float4 co3 = 0.0625f; + + if (src_y >= 2 && src_y < srcRows - 2 && x >= 2 && x < srcCols - 2) + { + sum = co3 * convert_float4(((__global ushort4*)((__global char4*)srcData + (src_y - 2) * srcStep / 4))[x]); + sum = sum + co2 * convert_float4(((__global ushort4*)((__global char4*)srcData + (src_y - 1) * srcStep / 4))[x]); + sum = sum + co1 * convert_float4(((__global ushort4*)((__global char4*)srcData + (src_y ) * srcStep / 4))[x]); + sum = sum + co2 * convert_float4(((__global ushort4*)((__global char4*)srcData + (src_y + 1) * srcStep / 4))[x]); + sum = sum + co3 * convert_float4(((__global ushort4*)((__global char4*)srcData + (src_y + 2) * srcStep / 4))[x]); + + smem[2 + get_local_id(0)] = sum; + + if (get_local_id(0) < 2) + { + const int left_x = x - 2; + + sum = co3 * convert_float4(((__global ushort4*)((__global char4*)srcData + (src_y - 2) * srcStep / 4))[left_x]); + sum = sum + co2 * convert_float4(((__global ushort4*)((__global char4*)srcData + (src_y - 1) * srcStep / 4))[left_x]); + sum = sum + co1 * convert_float4(((__global ushort4*)((__global char4*)srcData + (src_y ) * srcStep / 4))[left_x]); + sum = sum + co2 * convert_float4(((__global ushort4*)((__global char4*)srcData + (src_y + 1) * srcStep / 4))[left_x]); + sum = sum + co3 * convert_float4(((__global ushort4*)((__global char4*)srcData + (src_y + 2) * srcStep / 4))[left_x]); + + smem[get_local_id(0)] = sum; + } + + if (get_local_id(0) > 253) + { + const int right_x = x + 2; + + sum = co3 * convert_float4(((__global ushort4*)((__global char4*)srcData + (src_y - 2) * srcStep / 4))[right_x]); + sum = sum + co2 * convert_float4(((__global ushort4*)((__global char4*)srcData + (src_y - 1) * srcStep / 4))[right_x]); + sum = sum + co1 * convert_float4(((__global ushort4*)((__global char4*)srcData + (src_y ) * srcStep / 4))[right_x]); + sum = sum + co2 * convert_float4(((__global ushort4*)((__global char4*)srcData + (src_y + 1) * srcStep / 4))[right_x]); + sum = sum + co3 * convert_float4(((__global ushort4*)((__global char4*)srcData + (src_y + 2) * srcStep / 4))[right_x]); + + smem[4 + get_local_id(0)] = sum; + } + } + else + { + int col = idx_col(x, last_col); + + sum = co3 * convert_float4(((__global ushort4*)((__global char4*)srcData + idx_row(src_y - 2, last_row) * srcStep / 4))[col]); + sum = sum + co2 * convert_float4(((__global ushort4*)((__global char4*)srcData + idx_row(src_y - 1, last_row) * srcStep / 4))[col]); + sum = sum + co1 * convert_float4(((__global ushort4*)((__global char4*)srcData + idx_row(src_y , last_row) * srcStep / 4))[col]); + sum = sum + co2 * convert_float4(((__global ushort4*)((__global char4*)srcData + idx_row(src_y + 1, last_row) * srcStep / 4))[col]); + sum = sum + co3 * convert_float4(((__global ushort4*)((__global char4*)srcData + idx_row(src_y + 2, last_row) * srcStep / 4))[col]); + + smem[2 + get_local_id(0)] = sum; + + if (get_local_id(0) < 2) + { + const int left_x = x - 2; + + col = idx_col(left_x, last_col); + + sum = co3 * convert_float4(((__global ushort4*)((__global char4*)srcData + idx_row(src_y - 2, last_row) * srcStep / 4))[col]); + sum = sum + co2 * convert_float4(((__global ushort4*)((__global char4*)srcData + idx_row(src_y - 1, last_row) * srcStep / 4))[col]); + sum = sum + co1 * convert_float4(((__global ushort4*)((__global char4*)srcData + idx_row(src_y , last_row) * srcStep / 4))[col]); + sum = sum + co2 * convert_float4(((__global ushort4*)((__global char4*)srcData + idx_row(src_y + 1, last_row) * srcStep / 4))[col]); + sum = sum + co3 * convert_float4(((__global ushort4*)((__global char4*)srcData + idx_row(src_y + 2, last_row) * srcStep / 4))[col]); + + smem[get_local_id(0)] = sum; + } + + if (get_local_id(0) > 253) + { + const int right_x = x + 2; + + col = idx_col(right_x, last_col); + + sum = co3 * convert_float4(((__global ushort4*)((__global char4*)srcData + idx_row(src_y - 2, last_row) * srcStep / 4))[col]); + sum = sum + co2 * convert_float4(((__global ushort4*)((__global char4*)srcData + idx_row(src_y - 1, last_row) * srcStep / 4))[col]); + sum = sum + co1 * convert_float4(((__global ushort4*)((__global char4*)srcData + idx_row(src_y , last_row) * srcStep / 4))[col]); + sum = sum + co2 * convert_float4(((__global ushort4*)((__global char4*)srcData + idx_row(src_y + 1, last_row) * srcStep / 4))[col]); + sum = sum + co3 * convert_float4(((__global ushort4*)((__global char4*)srcData + idx_row(src_y + 2, last_row) * srcStep / 4))[col]); + + smem[4 + get_local_id(0)] = sum; + } + } + + barrier(CLK_LOCAL_MEM_FENCE); + + if (get_local_id(0) < 128) + { + const int tid2 = get_local_id(0) * 2; + + sum = co3 * smem[2 + tid2 - 2]; + sum = sum + co2 * smem[2 + tid2 - 1]; + sum = sum + co1 * smem[2 + tid2 ]; + sum = sum + co2 * smem[2 + tid2 + 1]; + sum = sum + co3 * smem[2 + tid2 + 2]; + + const int dst_x = (get_group_id(0) * get_local_size(0) + tid2) / 2; + + if (dst_x < dstCols) + dst[y * dstStep / 8 + dst_x] = convert_ushort4_sat_rte(sum); + } +} + +/////////////////////////////////////////////////////////////////////// +////////////////////////// CV_16SC1 ////////////////////////////////// +/////////////////////////////////////////////////////////////////////// + +__kernel void pyrDown_C1_D3(__global short * srcData, int srcStep, int srcRows, int srcCols, __global short *dst, int dstStep, int dstCols) +{ + const int x = get_global_id(0); + const int y = get_group_id(1); + + __local float smem[256 + 4]; + + float sum; + + const int src_y = 2*y; + const int last_row = srcRows - 1; + const int last_col = srcCols - 1; + + if (src_y >= 2 && src_y < srcRows - 2 && x >= 2 && x < srcCols - 2) + { + sum = 0.0625f * ((__global short*)((__global char*)srcData + (src_y - 2) * srcStep))[x]; + sum = sum + 0.25f * ((__global short*)((__global char*)srcData + (src_y - 1) * srcStep))[x]; + sum = sum + 0.375f * ((__global short*)((__global char*)srcData + (src_y ) * srcStep))[x]; + sum = sum + 0.25f * ((__global short*)((__global char*)srcData + (src_y + 1) * srcStep))[x]; + sum = sum + 0.0625f * ((__global short*)((__global char*)srcData + (src_y + 2) * srcStep))[x]; + + smem[2 + get_local_id(0)] = sum; + + if (get_local_id(0) < 2) + { + const int left_x = x - 2; + + sum = 0.0625f * ((__global short*)((__global char*)srcData + (src_y - 2) * srcStep))[left_x]; + sum = sum + 0.25f * ((__global short*)((__global char*)srcData + (src_y - 1) * srcStep))[left_x]; + sum = sum + 0.375f * ((__global short*)((__global char*)srcData + (src_y ) * srcStep))[left_x]; + sum = sum + 0.25f * ((__global short*)((__global char*)srcData + (src_y + 1) * srcStep))[left_x]; + sum = sum + 0.0625f * ((__global short*)((__global char*)srcData + (src_y + 2) * srcStep))[left_x]; + + smem[get_local_id(0)] = sum; + } + + if (get_local_id(0) > 253) + { + const int right_x = x + 2; + + sum = 0.0625f * ((__global short*)((__global char*)srcData + (src_y - 2) * srcStep))[right_x]; + sum = sum + 0.25f * ((__global short*)((__global char*)srcData + (src_y - 1) * srcStep))[right_x]; + sum = sum + 0.375f * ((__global short*)((__global char*)srcData + (src_y ) * srcStep))[right_x]; + sum = sum + 0.25f * ((__global short*)((__global char*)srcData + (src_y + 1) * srcStep))[right_x]; + sum = sum + 0.0625f * ((__global short*)((__global char*)srcData + (src_y + 2) * srcStep))[right_x]; + + smem[4 + get_local_id(0)] = sum; + } + } + else + { + int col = idx_col(x, last_col); + + sum = 0.0625f * ((__global short*)((__global char*)srcData + idx_row(src_y - 2, last_row) * srcStep))[col]; + sum = sum + 0.25f * ((__global short*)((__global char*)srcData + idx_row(src_y - 1, last_row) * srcStep))[col]; + sum = sum + 0.375f * ((__global short*)((__global char*)srcData + idx_row(src_y , last_row) * srcStep))[col]; + sum = sum + 0.25f * ((__global short*)((__global char*)srcData + idx_row(src_y + 1, last_row) * srcStep))[col]; + sum = sum + 0.0625f * ((__global short*)((__global char*)srcData + idx_row(src_y + 2, last_row) * srcStep))[col]; + + smem[2 + get_local_id(0)] = sum; + + if (get_local_id(0) < 2) + { + const int left_x = x - 2; + + col = idx_col(left_x, last_col); + + sum = 0.0625f * ((__global short*)((__global char*)srcData + idx_row(src_y - 2, last_row) * srcStep))[col]; + sum = sum + 0.25f * ((__global short*)((__global char*)srcData + idx_row(src_y - 1, last_row) * srcStep))[col]; + sum = sum + 0.375f * ((__global short*)((__global char*)srcData + idx_row(src_y , last_row) * srcStep))[col]; + sum = sum + 0.25f * ((__global short*)((__global char*)srcData + idx_row(src_y + 1, last_row) * srcStep))[col]; + sum = sum + 0.0625f * ((__global short*)((__global char*)srcData + idx_row(src_y + 2, last_row) * srcStep))[col]; + + smem[get_local_id(0)] = sum; + } + + if (get_local_id(0) > 253) + { + const int right_x = x + 2; + + col = idx_col(right_x, last_col); + + sum = 0.0625f * ((__global short*)((__global char*)srcData + idx_row(src_y - 2, last_row) * srcStep))[col]; + sum = sum + 0.25f * ((__global short*)((__global char*)srcData + idx_row(src_y - 1, last_row) * srcStep))[col]; + sum = sum + 0.375f * ((__global short*)((__global char*)srcData + idx_row(src_y , last_row) * srcStep))[col]; + sum = sum + 0.25f * ((__global short*)((__global char*)srcData + idx_row(src_y + 1, last_row) * srcStep))[col]; + sum = sum + 0.0625f * ((__global short*)((__global char*)srcData + idx_row(src_y + 2, last_row) * srcStep))[col]; + + smem[4 + get_local_id(0)] = sum; + } + } + + barrier(CLK_LOCAL_MEM_FENCE); + + if (get_local_id(0) < 128) + { + const int tid2 = get_local_id(0) * 2; + + sum = 0.0625f * smem[2 + tid2 - 2]; + sum = sum + 0.25f * smem[2 + tid2 - 1]; + sum = sum + 0.375f * smem[2 + tid2 ]; + sum = sum + 0.25f * smem[2 + tid2 + 1]; + sum = sum + 0.0625f * smem[2 + tid2 + 2]; + + const int dst_x = (get_group_id(0) * get_local_size(0) + tid2) / 2; + + if (dst_x < dstCols) + dst[y * dstStep / 2 + dst_x] = convert_short_sat_rte(sum); + } +} + +/////////////////////////////////////////////////////////////////////// +////////////////////////// CV_16SC4 ////////////////////////////////// +/////////////////////////////////////////////////////////////////////// + +__kernel void pyrDown_C4_D3(__global short4 * srcData, int srcStep, int srcRows, int srcCols, __global short4 *dst, int dstStep, int dstCols) +{ + const int x = get_global_id(0); + const int y = get_group_id(1); + + __local float4 smem[256 + 4]; + + float4 sum; + + const int src_y = 2*y; + const int last_row = srcRows - 1; + const int last_col = srcCols - 1; + + float4 co1 = 0.375f; + float4 co2 = 0.25f; + float4 co3 = 0.0625f; + + if (src_y >= 2 && src_y < srcRows - 2 && x >= 2 && x < srcCols - 2) + { + sum = co3 * convert_float4(((__global short4*)((__global char4*)srcData + (src_y - 2) * srcStep / 4))[x]); + sum = sum + co2 * convert_float4(((__global short4*)((__global char4*)srcData + (src_y - 1) * srcStep / 4))[x]); + sum = sum + co1 * convert_float4(((__global short4*)((__global char4*)srcData + (src_y ) * srcStep / 4))[x]); + sum = sum + co2 * convert_float4(((__global short4*)((__global char4*)srcData + (src_y + 1) * srcStep / 4))[x]); + sum = sum + co3 * convert_float4(((__global short4*)((__global char4*)srcData + (src_y + 2) * srcStep / 4))[x]); + + smem[2 + get_local_id(0)] = sum; + + if (get_local_id(0) < 2) + { + const int left_x = x - 2; + + sum = co3 * convert_float4(((__global short4*)((__global char4*)srcData + (src_y - 2) * srcStep / 4))[left_x]); + sum = sum + co2 * convert_float4(((__global short4*)((__global char4*)srcData + (src_y - 1) * srcStep / 4))[left_x]); + sum = sum + co1 * convert_float4(((__global short4*)((__global char4*)srcData + (src_y ) * srcStep / 4))[left_x]); + sum = sum + co2 * convert_float4(((__global short4*)((__global char4*)srcData + (src_y + 1) * srcStep / 4))[left_x]); + sum = sum + co3 * convert_float4(((__global short4*)((__global char4*)srcData + (src_y + 2) * srcStep / 4))[left_x]); + + smem[get_local_id(0)] = sum; + } + + if (get_local_id(0) > 253) + { + const int right_x = x + 2; + + sum = co3 * convert_float4(((__global short4*)((__global char4*)srcData + (src_y - 2) * srcStep / 4))[right_x]); + sum = sum + co2 * convert_float4(((__global short4*)((__global char4*)srcData + (src_y - 1) * srcStep / 4))[right_x]); + sum = sum + co1 * convert_float4(((__global short4*)((__global char4*)srcData + (src_y ) * srcStep / 4))[right_x]); + sum = sum + co2 * convert_float4(((__global short4*)((__global char4*)srcData + (src_y + 1) * srcStep / 4))[right_x]); + sum = sum + co3 * convert_float4(((__global short4*)((__global char4*)srcData + (src_y + 2) * srcStep / 4))[right_x]); + + smem[4 + get_local_id(0)] = sum; + } + } + else + { + int col = idx_col(x, last_col); + + sum = co3 * convert_float4(((__global short4*)((__global char4*)srcData + idx_row(src_y - 2, last_row) * srcStep / 4))[col]); + sum = sum + co2 * convert_float4(((__global short4*)((__global char4*)srcData + idx_row(src_y - 1, last_row) * srcStep / 4))[col]); + sum = sum + co1 * convert_float4(((__global short4*)((__global char4*)srcData + idx_row(src_y , last_row) * srcStep / 4))[col]); + sum = sum + co2 * convert_float4(((__global short4*)((__global char4*)srcData + idx_row(src_y + 1, last_row) * srcStep / 4))[col]); + sum = sum + co3 * convert_float4(((__global short4*)((__global char4*)srcData + idx_row(src_y + 2, last_row) * srcStep / 4))[col]); + + smem[2 + get_local_id(0)] = sum; + + if (get_local_id(0) < 2) + { + const int left_x = x - 2; + + col = idx_col(left_x, last_col); + + sum = co3 * convert_float4(((__global short4*)((__global char4*)srcData + idx_row(src_y - 2, last_row) * srcStep / 4))[col]); + sum = sum + co2 * convert_float4(((__global short4*)((__global char4*)srcData + idx_row(src_y - 1, last_row) * srcStep / 4))[col]); + sum = sum + co1 * convert_float4(((__global short4*)((__global char4*)srcData + idx_row(src_y , last_row) * srcStep / 4))[col]); + sum = sum + co2 * convert_float4(((__global short4*)((__global char4*)srcData + idx_row(src_y + 1, last_row) * srcStep / 4))[col]); + sum = sum + co3 * convert_float4(((__global short4*)((__global char4*)srcData + idx_row(src_y + 2, last_row) * srcStep / 4))[col]); + + smem[get_local_id(0)] = sum; + } + + if (get_local_id(0) > 253) + { + const int right_x = x + 2; + + col = idx_col(right_x, last_col); + + sum = co3 * convert_float4(((__global short4*)((__global char4*)srcData + idx_row(src_y - 2, last_row) * srcStep / 4))[col]); + sum = sum + co2 * convert_float4(((__global short4*)((__global char4*)srcData + idx_row(src_y - 1, last_row) * srcStep / 4))[col]); + sum = sum + co1 * convert_float4(((__global short4*)((__global char4*)srcData + idx_row(src_y , last_row) * srcStep / 4))[col]); + sum = sum + co2 * convert_float4(((__global short4*)((__global char4*)srcData + idx_row(src_y + 1, last_row) * srcStep / 4))[col]); + sum = sum + co3 * convert_float4(((__global short4*)((__global char4*)srcData + idx_row(src_y + 2, last_row) * srcStep / 4))[col]); + + smem[4 + get_local_id(0)] = sum; + } + } + + barrier(CLK_LOCAL_MEM_FENCE); + + if (get_local_id(0) < 128) + { + const int tid2 = get_local_id(0) * 2; + + sum = co3 * smem[2 + tid2 - 2]; + sum = sum + co2 * smem[2 + tid2 - 1]; + sum = sum + co1 * smem[2 + tid2 ]; + sum = sum + co2 * smem[2 + tid2 + 1]; + sum = sum + co3 * smem[2 + tid2 + 2]; + + const int dst_x = (get_group_id(0) * get_local_size(0) + tid2) / 2; + + if (dst_x < dstCols) + dst[y * dstStep / 8 + dst_x] = convert_short4_sat_rte(sum); + } +} + +/////////////////////////////////////////////////////////////////////// +////////////////////////// CV_32FC1 ////////////////////////////////// +/////////////////////////////////////////////////////////////////////// + __kernel void pyrDown_C1_D5(__global float * srcData, int srcStep, int srcRows, int srcCols, __global float *dst, int dstStep, int dstCols) { const int x = get_global_id(0); @@ -441,6 +890,10 @@ __kernel void pyrDown_C1_D5(__global float * srcData, int srcStep, int srcRows, } } +/////////////////////////////////////////////////////////////////////// +////////////////////////// CV_32FC4 ////////////////////////////////// +/////////////////////////////////////////////////////////////////////// + __kernel void pyrDown_C4_D5(__global float4 * srcData, int srcStep, int srcRows, int srcCols, __global float4 *dst, int dstStep, int dstCols) { const int x = get_global_id(0); @@ -454,16 +907,16 @@ __kernel void pyrDown_C4_D5(__global float4 * srcData, int srcStep, int srcRows, const int last_row = srcRows - 1; const int last_col = srcCols - 1; - float4 co1 = 0.375f;//(float4)(0.375f, 0.375f, 0.375f, 0.375f); - float4 co2 = 0.25f;//(float4)(0.25f, 0.25f, 0.25f, 0.25f); - float4 co3 = 0.0625f;//(float4)(0.0625f, 0.0625f, 0.0625f, 0.0625f); + float4 co1 = 0.375f; + float4 co2 = 0.25f; + float4 co3 = 0.0625f; if (src_y >= 2 && src_y < srcRows - 2 && x >= 2 && x < srcCols - 2) { sum = co3 * ((__global float4*)((__global char4*)srcData + (src_y - 2) * srcStep / 4))[x]; - sum = sum + co2 * ((__global float4*)((__global char4*)srcData + (src_y - 1) * srcStep / 4))[x]; - sum = sum + co1 * ((__global float4*)((__global char4*)srcData + (src_y ) * srcStep / 4))[x]; - sum = sum + co2 * ((__global float4*)((__global char4*)srcData + (src_y + 1) * srcStep / 4))[x]; + sum = sum + co2 * ((__global float4*)((__global char4*)srcData + (src_y - 1) * srcStep / 4))[x]; + sum = sum + co1 * ((__global float4*)((__global char4*)srcData + (src_y ) * srcStep / 4))[x]; + sum = sum + co2 * ((__global float4*)((__global char4*)srcData + (src_y + 1) * srcStep / 4))[x]; sum = sum + co3 * ((__global float4*)((__global char4*)srcData + (src_y + 2) * srcStep / 4))[x]; smem[2 + get_local_id(0)] = sum; @@ -473,9 +926,9 @@ __kernel void pyrDown_C4_D5(__global float4 * srcData, int srcStep, int srcRows, const int left_x = x - 2; sum = co3 * ((__global float4*)((__global char4*)srcData + (src_y - 2) * srcStep / 4))[left_x]; - sum = sum + co2 * ((__global float4*)((__global char4*)srcData + (src_y - 1) * srcStep / 4))[left_x]; - sum = sum + co1 * ((__global float4*)((__global char4*)srcData + (src_y ) * srcStep / 4))[left_x]; - sum = sum + co2 * ((__global float4*)((__global char4*)srcData + (src_y + 1) * srcStep / 4))[left_x]; + sum = sum + co2 * ((__global float4*)((__global char4*)srcData + (src_y - 1) * srcStep / 4))[left_x]; + sum = sum + co1 * ((__global float4*)((__global char4*)srcData + (src_y ) * srcStep / 4))[left_x]; + sum = sum + co2 * ((__global float4*)((__global char4*)srcData + (src_y + 1) * srcStep / 4))[left_x]; sum = sum + co3 * ((__global float4*)((__global char4*)srcData + (src_y + 2) * srcStep / 4))[left_x]; smem[get_local_id(0)] = sum; @@ -486,9 +939,9 @@ __kernel void pyrDown_C4_D5(__global float4 * srcData, int srcStep, int srcRows, const int right_x = x + 2; sum = co3 * ((__global float4*)((__global char4*)srcData + (src_y - 2) * srcStep / 4))[right_x]; - sum = sum + co2 * ((__global float4*)((__global char4*)srcData + (src_y - 1) * srcStep / 4))[right_x]; - sum = sum + co1 * ((__global float4*)((__global char4*)srcData + (src_y ) * srcStep / 4))[right_x]; - sum = sum + co2 * ((__global float4*)((__global char4*)srcData + (src_y + 1) * srcStep / 4))[right_x]; + sum = sum + co2 * ((__global float4*)((__global char4*)srcData + (src_y - 1) * srcStep / 4))[right_x]; + sum = sum + co1 * ((__global float4*)((__global char4*)srcData + (src_y ) * srcStep / 4))[right_x]; + sum = sum + co2 * ((__global float4*)((__global char4*)srcData + (src_y + 1) * srcStep / 4))[right_x]; sum = sum + co3 * ((__global float4*)((__global char4*)srcData + (src_y + 2) * srcStep / 4))[right_x]; smem[4 + get_local_id(0)] = sum; @@ -499,9 +952,9 @@ __kernel void pyrDown_C4_D5(__global float4 * srcData, int srcStep, int srcRows, int col = idx_col(x, last_col); sum = co3 * ((__global float4*)((__global char4*)srcData + idx_row(src_y - 2, last_row) * srcStep / 4))[col]; - sum = sum + co2 * ((__global float4*)((__global char4*)srcData + idx_row(src_y - 1, last_row) * srcStep / 4))[col]; - sum = sum + co1 * ((__global float4*)((__global char4*)srcData + idx_row(src_y , last_row) * srcStep / 4))[col]; - sum = sum + co2 * ((__global float4*)((__global char4*)srcData + idx_row(src_y + 1, last_row) * srcStep / 4))[col]; + sum = sum + co2 * ((__global float4*)((__global char4*)srcData + idx_row(src_y - 1, last_row) * srcStep / 4))[col]; + sum = sum + co1 * ((__global float4*)((__global char4*)srcData + idx_row(src_y , last_row) * srcStep / 4))[col]; + sum = sum + co2 * ((__global float4*)((__global char4*)srcData + idx_row(src_y + 1, last_row) * srcStep / 4))[col]; sum = sum + co3 * ((__global float4*)((__global char4*)srcData + idx_row(src_y + 2, last_row) * srcStep / 4))[col]; smem[2 + get_local_id(0)] = sum; @@ -513,9 +966,9 @@ __kernel void pyrDown_C4_D5(__global float4 * srcData, int srcStep, int srcRows, col = idx_col(left_x, last_col); sum = co3 * ((__global float4*)((__global char4*)srcData + idx_row(src_y - 2, last_row) * srcStep / 4))[col]; - sum = sum + co2 * ((__global float4*)((__global char4*)srcData + idx_row(src_y - 1, last_row) * srcStep / 4))[col]; - sum = sum + co1 * ((__global float4*)((__global char4*)srcData + idx_row(src_y , last_row) * srcStep / 4))[col]; - sum = sum + co2 * ((__global float4*)((__global char4*)srcData + idx_row(src_y + 1, last_row) * srcStep / 4))[col]; + sum = sum + co2 * ((__global float4*)((__global char4*)srcData + idx_row(src_y - 1, last_row) * srcStep / 4))[col]; + sum = sum + co1 * ((__global float4*)((__global char4*)srcData + idx_row(src_y , last_row) * srcStep / 4))[col]; + sum = sum + co2 * ((__global float4*)((__global char4*)srcData + idx_row(src_y + 1, last_row) * srcStep / 4))[col]; sum = sum + co3 * ((__global float4*)((__global char4*)srcData + idx_row(src_y + 2, last_row) * srcStep / 4))[col]; smem[get_local_id(0)] = sum; @@ -528,9 +981,9 @@ __kernel void pyrDown_C4_D5(__global float4 * srcData, int srcStep, int srcRows, col = idx_col(right_x, last_col); sum = co3 * ((__global float4*)((__global char4*)srcData + idx_row(src_y - 2, last_row) * srcStep / 4))[col]; - sum = sum + co2 * ((__global float4*)((__global char4*)srcData + idx_row(src_y - 1, last_row) * srcStep / 4))[col]; - sum = sum + co1 * ((__global float4*)((__global char4*)srcData + idx_row(src_y , last_row) * srcStep / 4))[col]; - sum = sum + co2 * ((__global float4*)((__global char4*)srcData + idx_row(src_y + 1, last_row) * srcStep / 4))[col]; + sum = sum + co2 * ((__global float4*)((__global char4*)srcData + idx_row(src_y - 1, last_row) * srcStep / 4))[col]; + sum = sum + co1 * ((__global float4*)((__global char4*)srcData + idx_row(src_y , last_row) * srcStep / 4))[col]; + sum = sum + co2 * ((__global float4*)((__global char4*)srcData + idx_row(src_y + 1, last_row) * srcStep / 4))[col]; sum = sum + co3 * ((__global float4*)((__global char4*)srcData + idx_row(src_y + 2, last_row) * srcStep / 4))[col]; smem[4 + get_local_id(0)] = sum; @@ -544,9 +997,9 @@ __kernel void pyrDown_C4_D5(__global float4 * srcData, int srcStep, int srcRows, const int tid2 = get_local_id(0) * 2; sum = co3 * smem[2 + tid2 - 2]; - sum = sum + co2 * smem[2 + tid2 - 1]; - sum = sum + co1 * smem[2 + tid2 ]; - sum = sum + co2 * smem[2 + tid2 + 1]; + sum = sum + co2 * smem[2 + tid2 - 1]; + sum = sum + co1 * smem[2 + tid2 ]; + sum = sum + co2 * smem[2 + tid2 + 1]; sum = sum + co3 * smem[2 + tid2 + 2]; const int dst_x = (get_group_id(0) * get_local_size(0) + tid2) / 2; diff --git a/modules/ocl/src/opencl/pyr_up.cl b/modules/ocl/src/opencl/pyr_up.cl index 4afa7b7104..f58205c02a 100644 --- a/modules/ocl/src/opencl/pyr_up.cl +++ b/modules/ocl/src/opencl/pyr_up.cl @@ -46,18 +46,18 @@ // //M*/ -//#pragma OPENCL EXTENSION cl_amd_printf : enable - uchar get_valid_uchar(float data) { return (uchar)(data <= 255 ? data : data > 0 ? 255 : 0); } + /////////////////////////////////////////////////////////////////////// ////////////////////////// CV_8UC1 ////////////////////////////////// /////////////////////////////////////////////////////////////////////// -__kernel void pyrUp_C1_D0(__global uchar* src,__global uchar* dst, - int srcRows,int dstRows,int srcCols,int dstCols, - int srcOffset,int dstOffset,int srcStep,int dstStep) + +__kernel void pyrUp_C1_D0(__global uchar* src, __global uchar* dst, + int srcRows, int dstRows, int srcCols, int dstCols, + int srcOffset, int dstOffset, int srcStep, int dstStep) { const int x = get_global_id(0); const int y = get_global_id(1); @@ -144,15 +144,15 @@ __kernel void pyrUp_C1_D0(__global uchar* src,__global uchar* dst, if ((x < dstCols) && (y < dstRows)) dst[x + y * dstStep] = convert_uchar_sat_rte(4.0f * sum); - } /////////////////////////////////////////////////////////////////////// ////////////////////////// CV_16UC1 ///////////////////////////////// /////////////////////////////////////////////////////////////////////// -__kernel void pyrUp_C1_D2(__global ushort* src,__global ushort* dst, - int srcRows,int dstRows,int srcCols,int dstCols, - int srcOffset,int dstOffset,int srcStep,int dstStep) + +__kernel void pyrUp_C1_D2(__global ushort* src, __global ushort* dst, + int srcRows, int dstRows, int srcCols, int dstCols, + int srcOffset, int dstOffset, int srcStep, int dstStep) { const int x = get_global_id(0); const int y = get_global_id(1); @@ -245,16 +245,116 @@ __kernel void pyrUp_C1_D2(__global ushort* src,__global ushort* dst, sum = sum + 0.0625f * s_dstPatch[2 + tidy + 2][get_local_id(0)]; if ((x < dstCols) && (y < dstRows)) - dst[x + y * dstStep] = convert_short_sat_rte(4.0f * sum); + dst[x + y * dstStep] = convert_ushort_sat_rte(4.0f * sum); +} + +/////////////////////////////////////////////////////////////////////// +////////////////////////// CV_16SC1 ///////////////////////////////// +/////////////////////////////////////////////////////////////////////// + +__kernel void pyrUp_C1_D3(__global short* src, __global short* dst, + int srcRows, int dstRows, int srcCols, int dstCols, + int srcOffset, int dstOffset, int srcStep, int dstStep) +{ + const int x = get_global_id(0); + const int y = get_global_id(1); + + __local float s_srcPatch[10][10]; + __local float s_dstPatch[20][16]; + + srcStep = srcStep >> 1; + dstStep = dstStep >> 1; + srcOffset = srcOffset >> 1; + dstOffset = dstOffset >> 1; + + if( get_local_id(0) < 10 && get_local_id(1) < 10 ) + { + int srcx = (int)(get_group_id(0) * get_local_size(0) / 2 + get_local_id(0)) - 1; + int srcy = (int)(get_group_id(1) * get_local_size(1) / 2 + get_local_id(1)) - 1; + srcx = abs(srcx); + srcx = min(srcCols - 1,srcx); + + srcy = abs(srcy); + srcy = min(srcRows -1 ,srcy); + + s_srcPatch[get_local_id(1)][get_local_id(0)] = (float)(src[srcx + srcy * srcStep]); + } + + barrier(CLK_LOCAL_MEM_FENCE); + + float sum = 0; + + const int evenFlag = (int)((get_local_id(0) & 1) == 0); + const int oddFlag = (int)((get_local_id(0) & 1) != 0); + const bool eveny = ((get_local_id(1) & 1) == 0); + const int tidx = get_local_id(0); + + if (eveny) + { + sum = sum + (evenFlag * 0.0625f) * s_srcPatch[1 + (get_local_id(1) >> 1)][1 + ((tidx - 2) >> 1)]; + sum = sum + ( oddFlag * 0.25f ) * s_srcPatch[1 + (get_local_id(1) >> 1)][1 + ((tidx - 1) >> 1)]; + sum = sum + (evenFlag * 0.375f ) * s_srcPatch[1 + (get_local_id(1) >> 1)][1 + ((tidx ) >> 1)]; + sum = sum + ( oddFlag * 0.25f ) * s_srcPatch[1 + (get_local_id(1) >> 1)][1 + ((tidx + 1) >> 1)]; + sum = sum + (evenFlag * 0.0625f) * s_srcPatch[1 + (get_local_id(1) >> 1)][1 + ((tidx + 2) >> 1)]; + } + + s_dstPatch[2 + get_local_id(1)][get_local_id(0)] = sum; + + if (get_local_id(1) < 2) + { + sum = 0; + + if (eveny) + { + sum = sum + (evenFlag * 0.0625f) * s_srcPatch[0][1 + ((tidx - 2) >> 1)]; + sum = sum + ( oddFlag * 0.25f ) * s_srcPatch[0][1 + ((tidx - 1) >> 1)]; + sum = sum + (evenFlag * 0.375f ) * s_srcPatch[0][1 + ((tidx ) >> 1)]; + sum = sum + ( oddFlag * 0.25f ) * s_srcPatch[0][1 + ((tidx + 1) >> 1)]; + sum = sum + (evenFlag * 0.0625f) * s_srcPatch[0][1 + ((tidx + 2) >> 1)]; + } + + s_dstPatch[get_local_id(1)][get_local_id(0)] = sum; + } + + if (get_local_id(1) > 13) + { + sum = 0; + + if (eveny) + { + sum = sum + (evenFlag * 0.0625f) * s_srcPatch[9][1 + ((tidx - 2) >> 1)]; + sum = sum + ( oddFlag * 0.25f ) * s_srcPatch[9][1 + ((tidx - 1) >> 1)]; + sum = sum + (evenFlag * 0.375f ) * s_srcPatch[9][1 + ((tidx ) >> 1)]; + sum = sum + ( oddFlag * 0.25f ) * s_srcPatch[9][1 + ((tidx + 1) >> 1)]; + sum = sum + (evenFlag * 0.0625f) * s_srcPatch[9][1 + ((tidx + 2) >> 1)]; + } + s_dstPatch[4 + get_local_id(1)][get_local_id(0)] = sum; + } + + barrier(CLK_LOCAL_MEM_FENCE); + + sum = 0; + + const int tidy = get_local_id(1); + + sum = sum + 0.0625f * s_dstPatch[2 + tidy - 2][get_local_id(0)]; + sum = sum + 0.25f * s_dstPatch[2 + tidy - 1][get_local_id(0)]; + sum = sum + 0.375f * s_dstPatch[2 + tidy ][get_local_id(0)]; + sum = sum + 0.25f * s_dstPatch[2 + tidy + 1][get_local_id(0)]; + sum = sum + 0.0625f * s_dstPatch[2 + tidy + 2][get_local_id(0)]; + + if ((x < dstCols) && (y < dstRows)) + dst[x + y * dstStep] = convert_short_sat_rte(4.0f * sum); } /////////////////////////////////////////////////////////////////////// ////////////////////////// CV_32FC1 ///////////////////////////////// /////////////////////////////////////////////////////////////////////// -__kernel void pyrUp_C1_D5(__global float* src,__global float* dst, - int srcRows,int dstRows,int srcCols,int dstCols, - int srcOffset,int dstOffset,int srcStep,int dstStep) + +__kernel void pyrUp_C1_D5(__global float* src, __global float* dst, + int srcRows, int dstRows, int srcCols, int dstCols, + int srcOffset, int dstOffset, int srcStep, int dstStep) { const int x = get_global_id(0); const int y = get_global_id(1); @@ -346,15 +446,15 @@ __kernel void pyrUp_C1_D5(__global float* src,__global float* dst, if ((x < dstCols) && (y < dstRows)) dst[x + y * dstStep] = (float)(4.0f * sum); - } /////////////////////////////////////////////////////////////////////// ////////////////////////// CV_8UC4 ////////////////////////////////// /////////////////////////////////////////////////////////////////////// -__kernel void pyrUp_C4_D0(__global uchar4* src,__global uchar4* dst, - int srcRows,int dstRows,int srcCols,int dstCols, - int srcOffset,int dstOffset,int srcStep,int dstStep) + +__kernel void pyrUp_C4_D0(__global uchar4* src, __global uchar4* dst, + int srcRows, int dstRows, int srcCols, int dstCols, + int srcOffset, int dstOffset, int srcStep, int dstStep) { const int x = get_global_id(0); const int y = get_global_id(1); @@ -451,17 +551,16 @@ __kernel void pyrUp_C4_D0(__global uchar4* src,__global uchar4* dst, sum = sum + co3 * s_dstPatch[2 + tidy + 2][tidx]; if ((x < dstCols) && (y < dstRows)) - { dst[x + y * dstStep] = convert_uchar4_sat_rte(4.0f * sum); - } } /////////////////////////////////////////////////////////////////////// ////////////////////////// CV_16UC4 ////////////////////////////////// /////////////////////////////////////////////////////////////////////// -__kernel void pyrUp_C4_D2(__global ushort4* src,__global ushort4* dst, - int srcRows,int dstRows,int srcCols,int dstCols, - int srcOffset,int dstOffset,int srcStep,int dstStep) + +__kernel void pyrUp_C4_D2(__global ushort4* src, __global ushort4* dst, + int srcRows, int dstRows, int srcCols, int dstCols, + int srcOffset, int dstOffset, int srcStep, int dstStep) { const int x = get_global_id(0); const int y = get_global_id(1); @@ -560,17 +659,123 @@ __kernel void pyrUp_C4_D2(__global ushort4* src,__global ushort4* dst, sum = sum + co3 * s_dstPatch[2 + tidy + 2][get_local_id(0)]; if ((x < dstCols) && (y < dstRows)) - { dst[x + y * dstStep] = convert_ushort4_sat_rte(4.0f * sum); +} + +/////////////////////////////////////////////////////////////////////// +////////////////////////// CV_16SC4 ////////////////////////////////// +/////////////////////////////////////////////////////////////////////// + +__kernel void pyrUp_C4_D3(__global short4* src, __global short4* dst, + int srcRows, int dstRows, int srcCols, int dstCols, + int srcOffset, int dstOffset, int srcStep, int dstStep) +{ + const int x = get_global_id(0); + const int y = get_global_id(1); + + __local float4 s_srcPatch[10][10]; + __local float4 s_dstPatch[20][16]; + + srcOffset >>= 3; + dstOffset >>= 3; + srcStep >>= 3; + dstStep >>= 3; + + if( get_local_id(0) < 10 && get_local_id(1) < 10 ) + { + int srcx = (int)(get_group_id(0) * get_local_size(0) / 2 + get_local_id(0)) - 1; + int srcy = (int)(get_group_id(1) * get_local_size(1) / 2 + get_local_id(1)) - 1; + + srcx = abs(srcx); + srcx = min(srcCols - 1,srcx); + + srcy = abs(srcy); + srcy = min(srcRows -1 ,srcy); + + s_srcPatch[get_local_id(1)][get_local_id(0)] = convert_float4(src[srcx + srcy * srcStep]); } + + barrier(CLK_LOCAL_MEM_FENCE); + + float4 sum = (float4)(0,0,0,0); + + const float4 evenFlag = (float4)((get_local_id(0) & 1) == 0); + const float4 oddFlag = (float4)((get_local_id(0) & 1) != 0); + const bool eveny = ((get_local_id(1) & 1) == 0); + const int tidx = get_local_id(0); + + float4 co1 = (float4)(0.375f, 0.375f, 0.375f, 0.375f); + float4 co2 = (float4)(0.25f, 0.25f, 0.25f, 0.25f); + float4 co3 = (float4)(0.0625f, 0.0625f, 0.0625f, 0.0625f); + + + if(eveny) + { + sum = sum + ( evenFlag* co3 ) * s_srcPatch[1 + (get_local_id(1) >> 1)][1 + ((tidx - 2) >> 1)]; + sum = sum + ( oddFlag * co2 ) * s_srcPatch[1 + (get_local_id(1) >> 1)][1 + ((tidx - 1) >> 1)]; + sum = sum + ( evenFlag* co1 ) * s_srcPatch[1 + (get_local_id(1) >> 1)][1 + ((tidx ) >> 1)]; + sum = sum + ( oddFlag * co2 ) * s_srcPatch[1 + (get_local_id(1) >> 1)][1 + ((tidx + 1) >> 1)]; + sum = sum + ( evenFlag* co3 ) * s_srcPatch[1 + (get_local_id(1) >> 1)][1 + ((tidx + 2) >> 1)]; + + } + + s_dstPatch[2 + get_local_id(1)][get_local_id(0)] = sum; + + if (get_local_id(1) < 2) + { + sum = 0; + + if (eveny) + { + sum = sum + (evenFlag * co3 ) * s_srcPatch[0][1 + ((tidx - 2) >> 1)]; + sum = sum + (oddFlag * co2 ) * s_srcPatch[0][1 + ((tidx - 1) >> 1)]; + sum = sum + (evenFlag * co1 ) * s_srcPatch[0][1 + ((tidx ) >> 1)]; + sum = sum + (oddFlag * co2 ) * s_srcPatch[0][1 + ((tidx + 1) >> 1)]; + sum = sum + (evenFlag * co3 ) * s_srcPatch[0][1 + ((tidx + 2) >> 1)]; + } + + s_dstPatch[get_local_id(1)][get_local_id(0)] = sum; + } + + if (get_local_id(1) > 13) + { + sum = 0; + + if (eveny) + { + sum = sum + (evenFlag * co3) * s_srcPatch[9][1 + ((tidx - 2) >> 1)]; + sum = sum + ( oddFlag * co2) * s_srcPatch[9][1 + ((tidx - 1) >> 1)]; + sum = sum + (evenFlag * co1) * s_srcPatch[9][1 + ((tidx ) >> 1)]; + sum = sum + ( oddFlag * co2) * s_srcPatch[9][1 + ((tidx + 1) >> 1)]; + sum = sum + (evenFlag * co3) * s_srcPatch[9][1 + ((tidx + 2) >> 1)]; + + } + s_dstPatch[4 + get_local_id(1)][get_local_id(0)] = sum; + } + + barrier(CLK_LOCAL_MEM_FENCE); + + sum = 0; + + const int tidy = get_local_id(1); + + sum = sum + co3 * s_dstPatch[2 + tidy - 2][get_local_id(0)]; + sum = sum + co2 * s_dstPatch[2 + tidy - 1][get_local_id(0)]; + sum = sum + co1 * s_dstPatch[2 + tidy ][get_local_id(0)]; + sum = sum + co2 * s_dstPatch[2 + tidy + 1][get_local_id(0)]; + sum = sum + co3 * s_dstPatch[2 + tidy + 2][get_local_id(0)]; + + if ((x < dstCols) && (y < dstRows)) + dst[x + y * dstStep] = convert_short4_sat_rte(4.0f * sum); } /////////////////////////////////////////////////////////////////////// ////////////////////////// CV_32FC4 ////////////////////////////////// /////////////////////////////////////////////////////////////////////// -__kernel void pyrUp_C4_D5(__global float4* src,__global float4* dst, - int srcRows,int dstRows,int srcCols,int dstCols, - int srcOffset,int dstOffset,int srcStep,int dstStep) + +__kernel void pyrUp_C4_D5(__global float4* src, __global float4* dst, + int srcRows, int dstRows, int srcCols, int dstCols, + int srcOffset, int dstOffset, int srcStep, int dstStep) { const int x = get_global_id(0); const int y = get_global_id(1); @@ -667,7 +872,5 @@ __kernel void pyrUp_C4_D5(__global float4* src,__global float4* dst, sum = sum + co3 * s_dstPatch[2 + tidy + 2][tidx]; if ((x < dstCols) && (y < dstRows)) - { dst[x + y * dstStep] = 4.0f * sum; - } } diff --git a/modules/ocl/src/pyrdown.cpp b/modules/ocl/src/pyrdown.cpp index 1e3e31de94..5043da05dc 100644 --- a/modules/ocl/src/pyrdown.cpp +++ b/modules/ocl/src/pyrdown.cpp @@ -73,24 +73,11 @@ static void pyrdown_run(const oclMat &src, const oclMat &dst) CV_Assert(src.depth() != CV_8S); Context *clCxt = src.clCxt; - //int channels = dst.channels(); - //int depth = dst.depth(); - string kernelName = "pyrDown"; - //int vector_lengths[4][7] = {{4, 0, 4, 4, 1, 1, 1}, - // {4, 0, 4, 4, 1, 1, 1}, - // {4, 0, 4, 4, 1, 1, 1}, - // {4, 0, 4, 4, 1, 1, 1} - //}; - - //size_t vector_length = vector_lengths[channels-1][depth]; - //int offset_cols = (dst.offset / dst.elemSize1()) & (vector_length - 1); - size_t localThreads[3] = { 256, 1, 1 }; size_t globalThreads[3] = { src.cols, dst.rows, 1}; - //int dst_step1 = dst.cols * dst.elemSize(); vector > args; args.push_back( make_pair( sizeof(cl_mem), (void *)&src.data )); args.push_back( make_pair( sizeof(cl_int), (void *)&src.step )); @@ -107,7 +94,9 @@ static void pyrdown_run(const oclMat &src, const oclMat &dst) void cv::ocl::pyrDown(const oclMat &src, oclMat &dst) { - CV_Assert(src.depth() <= CV_32F && src.channels() <= 4); + int depth = src.depth(), channels = src.channels(); + CV_Assert(depth == CV_8U || depth == CV_16U || depth == CV_16S || depth == CV_32F); + CV_Assert(channels == 1 || channels == 3 || channels == 4); dst.create((src.rows + 1) / 2, (src.cols + 1) / 2, src.type()); diff --git a/modules/ocl/src/pyrup.cpp b/modules/ocl/src/pyrup.cpp index 14f20aa88e..95a2915f41 100644 --- a/modules/ocl/src/pyrup.cpp +++ b/modules/ocl/src/pyrup.cpp @@ -61,6 +61,11 @@ namespace cv extern const char *pyr_up; void pyrUp(const cv::ocl::oclMat &src, cv::ocl::oclMat &dst) { + int depth = src.depth(), channels = src.channels(); + + CV_Assert(depth == CV_8U || depth == CV_16U || depth == CV_16S || depth == CV_32F); + CV_Assert(channels == 1 || channels == 3 || channels == 4); + dst.create(src.rows * 2, src.cols * 2, src.type()); Context *clCxt = src.clCxt; diff --git a/modules/ocl/test/test_pyramids.cpp b/modules/ocl/test/test_pyramids.cpp index 58179ac185..9070ee5aa7 100644 --- a/modules/ocl/test/test_pyramids.cpp +++ b/modules/ocl/test/test_pyramids.cpp @@ -57,60 +57,63 @@ using namespace std; PARAM_TEST_CASE(PyrBase, MatType, int) { - int type; + int depth; int channels; + Mat dst_cpu; oclMat gdst; + virtual void SetUp() { - type = GET_PARAM(0); + depth = GET_PARAM(0); channels = GET_PARAM(1); } - }; /////////////////////// PyrDown ////////////////////////// -struct PyrDown : PyrBase {}; + +typedef PyrBase PyrDown; TEST_P(PyrDown, Mat) { - for(int j = 0; j < LOOP_TIMES; j++) + for (int j = 0; j < LOOP_TIMES; j++) { Size size(MWIDTH, MHEIGHT); - Mat src = randomMat(size, CV_MAKETYPE(type, channels)); + Mat src = randomMat(size, CV_MAKETYPE(depth, channels)); oclMat gsrc(src); pyrDown(src, dst_cpu); pyrDown(gsrc, gdst); - EXPECT_MAT_NEAR(dst_cpu, Mat(gdst), type == CV_32F ? 1e-4f : 1.0f); + EXPECT_MAT_NEAR(dst_cpu, Mat(gdst), depth == CV_32F ? 1e-4f : 1.0f); } } INSTANTIATE_TEST_CASE_P(OCL_ImgProc, PyrDown, Combine( - Values(CV_8U, CV_32F), Values(1, 3, 4))); + Values(CV_8U, CV_16U, CV_16S, CV_32F), + Values(1, 3, 4))); /////////////////////// PyrUp ////////////////////////// -struct PyrUp : PyrBase {}; +typedef PyrBase PyrUp; TEST_P(PyrUp, Accuracy) { - for(int j = 0; j < LOOP_TIMES; j++) + for (int j = 0; j < LOOP_TIMES; j++) { Size size(MWIDTH, MHEIGHT); - Mat src = randomMat(size, CV_MAKETYPE(type, channels)); + Mat src = randomMat(size, CV_MAKETYPE(depth, channels)); oclMat gsrc(src); pyrUp(src, dst_cpu); pyrUp(gsrc, gdst); - EXPECT_MAT_NEAR(dst_cpu, Mat(gdst), (type == CV_32F ? 1e-4f : 1.0)); + EXPECT_MAT_NEAR(dst_cpu, Mat(gdst), (depth == CV_32F ? 1e-4f : 1.0)); } - } -INSTANTIATE_TEST_CASE_P(OCL_ImgProc, PyrUp, testing::Combine( - Values(CV_8U, CV_32F), Values(1, 3, 4))); +INSTANTIATE_TEST_CASE_P(OCL_ImgProc, PyrUp, Combine( + Values(CV_8U, CV_16U, CV_16S, CV_32F), + Values(1, 3, 4))); #endif // HAVE_OPENCL From 51ed1873de25ffd32abb6912429e259b8d6e8b51 Mon Sep 17 00:00:00 2001 From: Roman Donchenko Date: Tue, 10 Sep 2013 18:05:23 +0400 Subject: [PATCH 12/68] Eliminated unsequenced assignments to temp3 from dpstereo.cpp. They're unsequenced if CV_IMIN3 or CV_IMAX3 are invoked more than once in a single expression. --- modules/legacy/src/dpstereo.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/legacy/src/dpstereo.cpp b/modules/legacy/src/dpstereo.cpp index c920462aa8..7b7ebd6d3e 100644 --- a/modules/legacy/src/dpstereo.cpp +++ b/modules/legacy/src/dpstereo.cpp @@ -76,8 +76,8 @@ typedef struct _CvRightImData uchar min_val, max_val; } _CvRightImData; -#define CV_IMAX3(a,b,c) ((temp3 = (a) >= (b) ? (a) : (b)),(temp3 >= (c) ? temp3 : (c))) -#define CV_IMIN3(a,b,c) ((temp3 = (a) <= (b) ? (a) : (b)),(temp3 <= (c) ? temp3 : (c))) +#define CV_IMAX3(a,b,c) (std::max(std::max((a), (b)), (c))) +#define CV_IMIN3(a,b,c) (std::min(std::min((a), (b)), (c))) static void icvFindStereoCorrespondenceByBirchfieldDP( uchar* src1, uchar* src2, uchar* disparities, @@ -87,7 +87,7 @@ static void icvFindStereoCorrespondenceByBirchfieldDP( uchar* src1, uchar* src2, float _param3, float _param4, float _param5 ) { - int x, y, i, j, temp3; + int x, y, i, j; int d, s; int dispH = maxDisparity + 3; uchar *dispdata; From 98cb1dcc9a64efa4f0de1ff52fdd680b44ba8725 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Tue, 10 Sep 2013 18:05:45 +0400 Subject: [PATCH 13/68] MediaRecorder hint enabled for all Android devices with API level 14 and above. It increases performance on some devices like Nexus4. Target Android SDK version increased up to 14. --- modules/java/CMakeLists.txt | 2 +- modules/java/generator/src/java/android+JavaCameraView.java | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/java/CMakeLists.txt b/modules/java/CMakeLists.txt index a4d895a6b9..b15e98890d 100644 --- a/modules/java/CMakeLists.txt +++ b/modules/java/CMakeLists.txt @@ -193,7 +193,7 @@ if(ANDROID AND ANDROID_EXECUTABLE) set(lib_target_files ${ANDROID_LIB_PROJECT_FILES}) ocv_list_add_prefix(lib_target_files "${OpenCV_BINARY_DIR}/") - android_get_compatible_target(lib_target_sdk_target ${ANDROID_NATIVE_API_LEVEL} ${ANDROID_SDK_TARGET} 11) + android_get_compatible_target(lib_target_sdk_target ${ANDROID_NATIVE_API_LEVEL} ${ANDROID_SDK_TARGET} 14) configure_file("${CMAKE_CURRENT_SOURCE_DIR}/android_lib/${ANDROID_MANIFEST_FILE}" "${CMAKE_CURRENT_BINARY_DIR}/${ANDROID_MANIFEST_FILE}" @ONLY) add_custom_command(OUTPUT ${lib_target_files} "${OpenCV_BINARY_DIR}/${ANDROID_MANIFEST_FILE}" diff --git a/modules/java/generator/src/java/android+JavaCameraView.java b/modules/java/generator/src/java/android+JavaCameraView.java index f864e5370b..0acd85c194 100644 --- a/modules/java/generator/src/java/android+JavaCameraView.java +++ b/modules/java/generator/src/java/android+JavaCameraView.java @@ -146,6 +146,9 @@ public class JavaCameraView extends CameraBridgeViewBase implements PreviewCallb Log.d(TAG, "Set preview size to " + Integer.valueOf((int)frameSize.width) + "x" + Integer.valueOf((int)frameSize.height)); params.setPreviewSize((int)frameSize.width, (int)frameSize.height); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) + params.setRecordingHint(true); + List FocusModes = params.getSupportedFocusModes(); if (FocusModes != null && FocusModes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO)) { From 2e0f613c38099cc0fd06b8e8e31b46fe9fe410f9 Mon Sep 17 00:00:00 2001 From: Jin Ma Date: Wed, 11 Sep 2013 10:19:02 +0800 Subject: [PATCH 14/68] Added performance for Kalman Filter. --- modules/ocl/perf/perf_kalman.cpp | 94 ++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 modules/ocl/perf/perf_kalman.cpp diff --git a/modules/ocl/perf/perf_kalman.cpp b/modules/ocl/perf/perf_kalman.cpp new file mode 100644 index 0000000000..bed346cf0c --- /dev/null +++ b/modules/ocl/perf/perf_kalman.cpp @@ -0,0 +1,94 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2010-2012, Multicoreware, Inc., all rights reserved. +// Copyright (C) 2010-2012, Advanced Micro Devices, Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// @Authors +// Fangfang Bai, fangfang@multicorewareinc.com +// Jin Ma, jin@multicorewareinc.com +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other oclMaterials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors as is and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ +#include "perf_precomp.hpp" +using namespace perf; +using namespace std; +using namespace cv::ocl; +using namespace cv; +using std::tr1::tuple; +using std::tr1::get; +///////////// Kalman Filter //////////////////////// + +typedef tuple KalmanFilterType; +typedef TestBaseWithParam KalmanFilterFixture; + +PERF_TEST_P(KalmanFilterFixture, KalmanFilter, + ::testing::Values(1000, 1500)) +{ + KalmanFilterType params = GetParam(); + const int dim = get<0>(params); + + cv::Mat sample(dim, 1, CV_32FC1), dresult; + randu(sample, -1, 1); + cv::ocl::oclMat dsample(sample); + + cv::Mat statePre_; + + if(RUN_PLAIN_IMPL) + { + TEST_CYCLE() + { + cv::KalmanFilter kalman; + kalman.init(dim, dim); + kalman.correct(sample); + kalman.predict(); + statePre_ = kalman.statePre; + } + SANITY_CHECK(statePre_); + }else if(RUN_OCL_IMPL) + { + OCL_TEST_CYCLE() + { + cv::ocl::KalmanFilter kalman_ocl; + kalman_ocl.init(dim, dim); + kalman_ocl.correct(dsample); + kalman_ocl.predict(); + kalman_ocl.statePre.download(statePre_); + } + SANITY_CHECK(statePre_); + }else + OCL_PERF_ELSE +} \ No newline at end of file From b9d7e67589ae716ff063f5038004a0e05ec2cfa4 Mon Sep 17 00:00:00 2001 From: Jin Ma Date: Wed, 11 Sep 2013 11:28:36 +0800 Subject: [PATCH 15/68] Removed whitespace. --- modules/ocl/perf/perf_kalman.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ocl/perf/perf_kalman.cpp b/modules/ocl/perf/perf_kalman.cpp index bed346cf0c..67bbb92380 100644 --- a/modules/ocl/perf/perf_kalman.cpp +++ b/modules/ocl/perf/perf_kalman.cpp @@ -83,7 +83,7 @@ PERF_TEST_P(KalmanFilterFixture, KalmanFilter, OCL_TEST_CYCLE() { cv::ocl::KalmanFilter kalman_ocl; - kalman_ocl.init(dim, dim); + kalman_ocl.init(dim, dim); kalman_ocl.correct(dsample); kalman_ocl.predict(); kalman_ocl.statePre.download(statePre_); From f4d0c9b13c524313f6be2357695112081e2d210f Mon Sep 17 00:00:00 2001 From: Jin Ma Date: Thu, 12 Sep 2013 09:43:30 +0800 Subject: [PATCH 16/68] Revised the code according to the feedback of the community. --- modules/ocl/perf/perf_kalman.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/modules/ocl/perf/perf_kalman.cpp b/modules/ocl/perf/perf_kalman.cpp index 67bbb92380..b5f713be95 100644 --- a/modules/ocl/perf/perf_kalman.cpp +++ b/modules/ocl/perf/perf_kalman.cpp @@ -63,32 +63,31 @@ PERF_TEST_P(KalmanFilterFixture, KalmanFilter, cv::Mat sample(dim, 1, CV_32FC1), dresult; randu(sample, -1, 1); - cv::ocl::oclMat dsample(sample); cv::Mat statePre_; if(RUN_PLAIN_IMPL) { + cv::KalmanFilter kalman; TEST_CYCLE() { - cv::KalmanFilter kalman; kalman.init(dim, dim); kalman.correct(sample); kalman.predict(); - statePre_ = kalman.statePre; } - SANITY_CHECK(statePre_); + statePre_ = kalman.statePre; }else if(RUN_OCL_IMPL) { + cv::ocl::oclMat dsample(sample); + cv::ocl::KalmanFilter kalman_ocl; OCL_TEST_CYCLE() { - cv::ocl::KalmanFilter kalman_ocl; kalman_ocl.init(dim, dim); kalman_ocl.correct(dsample); kalman_ocl.predict(); - kalman_ocl.statePre.download(statePre_); } - SANITY_CHECK(statePre_); + kalman_ocl.statePre.download(statePre_); }else OCL_PERF_ELSE + SANITY_CHECK(statePre_); } \ No newline at end of file From 3fc6e27a69773479c1f3d74799a2df1bc8146b62 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Mon, 2 Sep 2013 17:52:07 +0400 Subject: [PATCH 17/68] Some improvements in ABI compatibility checker config generator. JNI check became optional; Pathes to SDK, NDK and Java API level can be defined by command line. --- platforms/scripts/ABI_compat_generator.py | 37 ++++++++++++++--------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/platforms/scripts/ABI_compat_generator.py b/platforms/scripts/ABI_compat_generator.py index d7cc3728aa..c518589d97 100755 --- a/platforms/scripts/ABI_compat_generator.py +++ b/platforms/scripts/ABI_compat_generator.py @@ -47,7 +47,7 @@ def GetClasses(root, prefix): def GetJavaHHeaders(): - print('\nGenerating JNI headers for Java API ...') + print('Generating JNI headers for Java API ...') javahHeaders = os.path.join(managerDir, 'javah_generated_headers') if os.path.exists(javahHeaders): @@ -70,7 +70,7 @@ def GetJavaHHeaders(): os.system('javah -d %s -classpath %s:%s %s' % (javahHeaders, classPath, \ AndroidJavaDeps, currentClass)) - print('\nBuilding JNI headers list ...') + print('Building JNI headers list ...') jniHeaders = GetHeaderFiles(javahHeaders) return jniHeaders @@ -94,10 +94,10 @@ def GetOpenCVModules(): -def FindHeaders(): +def FindHeaders(includeJni): headers = [] - print('\nBuilding Native OpenCV header list ...') + print('Building Native OpenCV header list ...') cppHeadersFolder = os.path.join(managerDir, 'sdk/native/jni/include/opencv2') @@ -124,7 +124,8 @@ def FindHeaders(): 'sdk/native/jni/include/opencv')) headers += cHeaders - headers += GetJavaHHeaders() + if (includeJni): + headers += GetJavaHHeaders() return headers @@ -198,24 +199,32 @@ def WriteXml(version, headers, includes, libraries): if __name__ == '__main__': - usage = '%prog ' + usage = '%prog [options] ' parser = OptionParser(usage = usage) + parser.add_option('--exclude-jni', dest='excludeJni', action="store_true", default=False, metavar="EXCLUDE_JNI", help='Exclude headers for all JNI functions') + parser.add_option('--sdk', dest='sdk', default='~/NVPACK/android-sdk-linux', metavar="PATH", help='Android SDK path') + parser.add_option('--ndk', dest='ndk', default='/opt/android-ndk-r8c', metavar="PATH", help='Android NDK path') + parser.add_option('--java-api-level', dest='java_api_level', default='14', metavar="JAVA_API_LEVEL", help='Java API level for generating JNI headers') + + (options, args) = parser.parse_args() - args = parser.parse_args() if 2 != len(args): parser.print_help() quit() - managerDir = args[1][0] - version = args[1][1] + managerDir = args[0] + version = args[1] + + include_jni = not options.excludeJni + print 'Include Jni headers: %s' % (include_jni) - NDK_path = '/opt/android-ndk-r8c' - print '\nUsing Android NDK from "%s"' % NDK_path + NDK_path = options.ndk + print 'Using Android NDK from "%s"' % NDK_path - SDK_path = '~/NVPACK/android-sdk-linux' - print '\nUsing Android SDK from "%s"' % SDK_path + SDK_path = options.sdk + print 'Using Android SDK from "%s"' % SDK_path - headers = FindHeaders() + headers = FindHeaders(include_jni) includes = FindIncludes() From fec697b390dfede92f415dc0f44b2634beba121e Mon Sep 17 00:00:00 2001 From: Roman Donchenko Date: Thu, 12 Sep 2013 12:31:13 +0400 Subject: [PATCH 18/68] Silenced a few -Wcast-align warnings in the headers. We don't use -Wcast-align ourselves, but the headers trigger warnings for those users who do, which is not nice. --- modules/core/include/opencv2/core/core_c.h | 2 +- modules/core/include/opencv2/core/mat.hpp | 6 +++--- modules/core/include/opencv2/core/operations.hpp | 4 ++-- modules/core/include/opencv2/core/types_c.h | 8 ++++---- modules/flann/include/opencv2/flann/lsh_table.h | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/modules/core/include/opencv2/core/core_c.h b/modules/core/include/opencv2/core/core_c.h index df763ab9a4..d4182d2f73 100644 --- a/modules/core/include/opencv2/core/core_c.h +++ b/modules/core/include/opencv2/core/core_c.h @@ -1129,7 +1129,7 @@ CVAPI(void) cvSetRemove( CvSet* set_header, int index ); NULL is returned */ CV_INLINE CvSetElem* cvGetSetElem( const CvSet* set_header, int idx ) { - CvSetElem* elem = (CvSetElem*)cvGetSeqElem( (CvSeq*)set_header, idx ); + CvSetElem* elem = (CvSetElem*)(void *)cvGetSeqElem( (CvSeq*)set_header, idx ); return elem && CV_IS_SET_ELEM( elem ) ? elem : 0; } diff --git a/modules/core/include/opencv2/core/mat.hpp b/modules/core/include/opencv2/core/mat.hpp index f798d7f4a0..4cb12cccc1 100644 --- a/modules/core/include/opencv2/core/mat.hpp +++ b/modules/core/include/opencv2/core/mat.hpp @@ -2263,10 +2263,10 @@ template inline const _Tp& SparseMat::value(const Node* n) const { return *(const _Tp*)((const uchar*)n + hdr->valueOffset); } inline SparseMat::Node* SparseMat::node(size_t nidx) -{ return (Node*)&hdr->pool[nidx]; } +{ return (Node*)(void*)&hdr->pool[nidx]; } inline const SparseMat::Node* SparseMat::node(size_t nidx) const -{ return (const Node*)&hdr->pool[nidx]; } +{ return (const Node*)(void*)&hdr->pool[nidx]; } inline SparseMatIterator SparseMat::begin() { return SparseMatIterator(this); } @@ -2327,7 +2327,7 @@ template inline const _Tp& SparseMatConstIterator::value() const inline const SparseMat::Node* SparseMatConstIterator::node() const { return ptr && m && m->hdr ? - (const SparseMat::Node*)(ptr - m->hdr->valueOffset) : 0; + (const SparseMat::Node*)(void*)(ptr - m->hdr->valueOffset) : 0; } inline SparseMatConstIterator SparseMatConstIterator::operator ++(int) diff --git a/modules/core/include/opencv2/core/operations.hpp b/modules/core/include/opencv2/core/operations.hpp index d3b80a0040..e100f754eb 100644 --- a/modules/core/include/opencv2/core/operations.hpp +++ b/modules/core/include/opencv2/core/operations.hpp @@ -3147,10 +3147,10 @@ inline FileNodeIterator FileNode::end() const } inline FileNode FileNodeIterator::operator *() const -{ return FileNode(fs, (const CvFileNode*)reader.ptr); } +{ return FileNode(fs, (const CvFileNode*)(void*)reader.ptr); } inline FileNode FileNodeIterator::operator ->() const -{ return FileNode(fs, (const CvFileNode*)reader.ptr); } +{ return FileNode(fs, (const CvFileNode*)(void*)reader.ptr); } template static inline FileNodeIterator& operator >> (FileNodeIterator& it, _Tp& value) { read( *it, value, _Tp()); return ++it; } diff --git a/modules/core/include/opencv2/core/types_c.h b/modules/core/include/opencv2/core/types_c.h index 27e53cd001..e3e755ea63 100644 --- a/modules/core/include/opencv2/core/types_c.h +++ b/modules/core/include/opencv2/core/types_c.h @@ -766,11 +766,11 @@ CV_INLINE double cvmGet( const CvMat* mat, int row, int col ) (unsigned)col < (unsigned)mat->cols ); if( type == CV_32FC1 ) - return ((float*)(mat->data.ptr + (size_t)mat->step*row))[col]; + return ((float*)(void*)(mat->data.ptr + (size_t)mat->step*row))[col]; else { assert( type == CV_64FC1 ); - return ((double*)(mat->data.ptr + (size_t)mat->step*row))[col]; + return ((double*)(void*)(mat->data.ptr + (size_t)mat->step*row))[col]; } } @@ -783,11 +783,11 @@ CV_INLINE void cvmSet( CvMat* mat, int row, int col, double value ) (unsigned)col < (unsigned)mat->cols ); if( type == CV_32FC1 ) - ((float*)(mat->data.ptr + (size_t)mat->step*row))[col] = (float)value; + ((float*)(void*)(mat->data.ptr + (size_t)mat->step*row))[col] = (float)value; else { assert( type == CV_64FC1 ); - ((double*)(mat->data.ptr + (size_t)mat->step*row))[col] = (double)value; + ((double*)(void*)(mat->data.ptr + (size_t)mat->step*row))[col] = (double)value; } } diff --git a/modules/flann/include/opencv2/flann/lsh_table.h b/modules/flann/include/opencv2/flann/lsh_table.h index a30642a480..b0f3223851 100644 --- a/modules/flann/include/opencv2/flann/lsh_table.h +++ b/modules/flann/include/opencv2/flann/lsh_table.h @@ -386,7 +386,7 @@ inline size_t LshTable::getKey(const unsigned char* feature) cons { // no need to check if T is dividable by sizeof(size_t) like in the Hamming // distance computation as we have a mask - const size_t* feature_block_ptr = reinterpret_cast (feature); + const size_t* feature_block_ptr = reinterpret_cast ((const void*)feature); // Figure out the subsignature of the feature // Given the feature ABCDEF, and the mask 001011, the output will be From f20cc2bce88e3dc00b398f15914deb716f795491 Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Wed, 11 Sep 2013 13:35:39 +0400 Subject: [PATCH 19/68] extended ocl::convertTo --- modules/ocl/src/matrix_operations.cpp | 56 +-- modules/ocl/src/opencl/operator_convertTo.cl | 356 +------------------ modules/ocl/test/test_matrix_operation.cpp | 311 ++++++++-------- modules/ocl/test/utility.hpp | 16 +- 4 files changed, 202 insertions(+), 537 deletions(-) diff --git a/modules/ocl/src/matrix_operations.cpp b/modules/ocl/src/matrix_operations.cpp index 82189b71e5..cd09f44027 100644 --- a/modules/ocl/src/matrix_operations.cpp +++ b/modules/ocl/src/matrix_operations.cpp @@ -382,40 +382,50 @@ void cv::ocl::oclMat::copyTo( oclMat &mat, const oclMat &mask) const /////////////////////////////////////////////////////////////////////////// static void convert_run(const oclMat &src, oclMat &dst, double alpha, double beta) { - string kernelName = "convert_to_S"; - stringstream idxStr; - idxStr << src.depth(); - kernelName += idxStr.str(); + string kernelName = "convert_to"; float alpha_f = alpha, beta_f = beta; + int sdepth = src.depth(), ddepth = dst.depth(); + int sstep1 = (int)src.step1(), dstep1 = (int)dst.step1(); + int cols1 = src.cols * src.oclchannels(); + + char buildOptions[150], convertString[50]; + const char * typeMap[] = { "uchar", "char", "ushort", "short", "int", "float", "double" }; + sprintf(convertString, "convert_%s_sat_rte", typeMap[ddepth]); + sprintf(buildOptions, "-D srcT=%s -D dstT=%s -D convertToDstType=%s", typeMap[sdepth], + typeMap[ddepth], CV_32F == ddepth || ddepth == CV_64F ? "" : convertString); + CV_DbgAssert(src.rows == dst.rows && src.cols == dst.cols); vector > args; - size_t localThreads[3] = {16, 16, 1}; - size_t globalThreads[3]; - globalThreads[0] = (dst.cols + localThreads[0] - 1) / localThreads[0] * localThreads[0]; - globalThreads[1] = (dst.rows + localThreads[1] - 1) / localThreads[1] * localThreads[1]; - globalThreads[2] = 1; - int dststep_in_pixel = dst.step / dst.elemSize(), dstoffset_in_pixel = dst.offset / dst.elemSize(); - int srcstep_in_pixel = src.step / src.elemSize(), srcoffset_in_pixel = src.offset / src.elemSize(); - if(dst.type() == CV_8UC1) - { - globalThreads[0] = ((dst.cols + 4) / 4 + localThreads[0]) / localThreads[0] * localThreads[0]; - } + + size_t localThreads[3] = { 16, 16, 1 }; + size_t globalThreads[3] = { divUp(cols1, localThreads[0]) * localThreads[0], + divUp(dst.rows, localThreads[1]) * localThreads[1], 1 }; + + int doffset1 = dst.offset / dst.elemSize1(); + int soffset1 = src.offset / src.elemSize1(); + args.push_back( make_pair( sizeof(cl_mem) , (void *)&src.data )); args.push_back( make_pair( sizeof(cl_mem) , (void *)&dst.data )); - args.push_back( make_pair( sizeof(cl_int) , (void *)&src.cols )); + args.push_back( make_pair( sizeof(cl_int) , (void *)&cols1 )); args.push_back( make_pair( sizeof(cl_int) , (void *)&src.rows )); - args.push_back( make_pair( sizeof(cl_int) , (void *)&srcstep_in_pixel )); - args.push_back( make_pair( sizeof(cl_int) , (void *)&srcoffset_in_pixel )); - args.push_back( make_pair( sizeof(cl_int) , (void *)&dststep_in_pixel )); - args.push_back( make_pair( sizeof(cl_int) , (void *)&dstoffset_in_pixel )); + args.push_back( make_pair( sizeof(cl_int) , (void *)&sstep1 )); + args.push_back( make_pair( sizeof(cl_int) , (void *)&soffset1 )); + args.push_back( make_pair( sizeof(cl_int) , (void *)&dstep1 )); + args.push_back( make_pair( sizeof(cl_int) , (void *)&doffset1 )); args.push_back( make_pair( sizeof(cl_float) , (void *)&alpha_f )); args.push_back( make_pair( sizeof(cl_float) , (void *)&beta_f )); + openCLExecuteKernel(dst.clCxt , &operator_convertTo, kernelName, globalThreads, - localThreads, args, dst.oclchannels(), dst.depth()); + localThreads, args, -1, -1, buildOptions); } void cv::ocl::oclMat::convertTo( oclMat &dst, int rtype, double alpha, double beta ) const { - //cout << "cv::ocl::oclMat::convertTo()" << endl; + if (!clCxt->supportsFeature(Context::CL_DOUBLE) && + (depth() == CV_64F || dst.depth() == CV_64F)) + { + CV_Error(CV_GpuNotSupported, "Selected device don't support double\r\n"); + return; + } bool noScale = fabs(alpha - 1) < std::numeric_limits::epsilon() && fabs(beta) < std::numeric_limits::epsilon(); @@ -425,7 +435,6 @@ void cv::ocl::oclMat::convertTo( oclMat &dst, int rtype, double alpha, double be else rtype = CV_MAKETYPE(CV_MAT_DEPTH(rtype), channels()); - //int scn = channels(); int sdepth = depth(), ddepth = CV_MAT_DEPTH(rtype); if( sdepth == ddepth && noScale ) { @@ -447,7 +456,6 @@ void cv::ocl::oclMat::convertTo( oclMat &dst, int rtype, double alpha, double be /////////////////////////////////////////////////////////////////////////// oclMat &cv::ocl::oclMat::operator = (const Scalar &s) { - //cout << "cv::ocl::oclMat::=" << endl; setTo(s); return *this; } diff --git a/modules/ocl/src/opencl/operator_convertTo.cl b/modules/ocl/src/opencl/operator_convertTo.cl index 1a8dd04b93..278d41f7ca 100644 --- a/modules/ocl/src/opencl/operator_convertTo.cl +++ b/modules/ocl/src/opencl/operator_convertTo.cl @@ -33,352 +33,28 @@ // the use of this software, even if advised of the possibility of such damage. // // -#define F float -#define F2 float2 -#define F4 float4 -__kernel void convert_to_S4_C1_D0( - __global const int* restrict srcMat, - __global uchar* dstMat, - int cols, - int rows, - int srcStep_in_pixel, - int srcoffset_in_pixel, - int dstStep_in_pixel, - int dstoffset_in_pixel, - F alpha, - F beta) -{ - int x=get_global_id(0)<<2; - int y=get_global_id(1); - //int src_addr_start = mad24(y,srcStep_in_pixel,srcoffset_in_pixel); - //int src_addr_end = mad24(y,srcStep_in_pixel,cols+srcoffset_in_pixel); - int off_src = (dstoffset_in_pixel & 3); - int srcidx = mad24(y,srcStep_in_pixel,x+ srcoffset_in_pixel - off_src); - int dst_addr_start = mad24(y,dstStep_in_pixel,dstoffset_in_pixel); - int dst_addr_end = mad24(y,dstStep_in_pixel,cols+dstoffset_in_pixel); - int dstidx = mad24(y,dstStep_in_pixel,x+ dstoffset_in_pixel & (int)0xfffffffc); - if(x+3get_rng(); - cv::Size size(MWIDTH, MHEIGHT); - mat = randomMat(rng, size, type, 5, 16, false); - dst = randomMat(rng, size, type, 5, 16, false); + mat = randomMat(rng, randomSize(MIN_VALUE, MAX_VALUE), src_type, 5, 136, false); + dst = randomMat(rng, use_roi ? randomSize(MIN_VALUE, MAX_VALUE) : mat.size(), dst_type, 5, 136, false); } void random_roi() { -#ifdef RANDOMROI - //randomize ROI - cv::RNG &rng = TS::ptr()->get_rng(); - roicols = rng.uniform(1, mat.cols); - roirows = rng.uniform(1, mat.rows); - srcx = rng.uniform(0, mat.cols - roicols); - srcy = rng.uniform(0, mat.rows - roirows); - dstx = rng.uniform(0, dst.cols - roicols); - dsty = rng.uniform(0, dst.rows - roirows); -#else - roicols = mat.cols; - roirows = mat.rows; - srcx = 0; - srcy = 0; - dstx = 0; - dsty = 0; -#endif + if (use_roi) + { + // randomize ROI + cv::RNG &rng = TS::ptr()->get_rng(); + roicols = rng.uniform(1, MIN_VALUE); + roirows = rng.uniform(1, MIN_VALUE); + srcx = rng.uniform(0, mat.cols - roicols); + srcy = rng.uniform(0, mat.rows - roirows); + dstx = rng.uniform(0, dst.cols - roicols); + dsty = rng.uniform(0, dst.rows - roirows); + } + else + { + roicols = mat.cols; + roirows = mat.rows; + srcx = srcy = 0; + dstx = dsty = 0; + } mat_roi = mat(Rect(srcx, srcy, roicols, roirows)); - dst_roi = dst(Rect(dstx, dsty, roicols, roirows)); + dst_roi = dst(Rect(dstx, dsty, roicols, roirows)); gdst_whole = dst; gdst = gdst_whole(Rect(dstx, dsty, roicols, roirows)); @@ -123,30 +127,28 @@ PARAM_TEST_CASE(ConvertToTestBase, MatType, MatType) } }; - -struct ConvertTo : ConvertToTestBase {}; +typedef ConvertToTestBase ConvertTo; TEST_P(ConvertTo, Accuracy) { - for(int j = 0; j < LOOP_TIMES; j++) + for (int j = 0; j < LOOP_TIMES; j++) { random_roi(); mat_roi.convertTo(dst_roi, dst_type); gmat.convertTo(gdst, dst_type); - EXPECT_MAT_NEAR(dst, Mat(gdst_whole), 0.0); + EXPECT_MAT_NEAR(dst, Mat(gdst_whole), src_depth == CV_64F ? 1.0 : 0.0); + EXPECT_MAT_NEAR(dst_roi, Mat(gdst), src_depth == CV_64F ? 1.0 : 0.0); } } - - - ///////////////////////////////////////////copyto///////////////////////////////////////////////////////////// PARAM_TEST_CASE(CopyToTestBase, MatType, bool) { int type; + bool use_roi; cv::Mat mat; cv::Mat mask; @@ -162,15 +164,15 @@ PARAM_TEST_CASE(CopyToTestBase, MatType, bool) int maskx; int masky; - //src mat with roi + // src mat with roi cv::Mat mat_roi; cv::Mat mask_roi; cv::Mat dst_roi; - //ocl dst mat for testing + // ocl dst mat for testing cv::ocl::oclMat gdst_whole; - //ocl mat with roi + // ocl mat with roi cv::ocl::oclMat gmat; cv::ocl::oclMat gdst; cv::ocl::oclMat gmask; @@ -178,45 +180,45 @@ PARAM_TEST_CASE(CopyToTestBase, MatType, bool) virtual void SetUp() { type = GET_PARAM(0); + use_roi = GET_PARAM(1); cv::RNG &rng = TS::ptr()->get_rng(); cv::Size size(MWIDTH, MHEIGHT); mat = randomMat(rng, size, type, 5, 16, false); - dst = randomMat(rng, size, type, 5, 16, false); + dst = randomMat(rng, size, type, 5, 16, false); mask = randomMat(rng, size, CV_8UC1, 0, 2, false); cv::threshold(mask, mask, 0.5, 255., CV_8UC1); - } void random_roi() { -#ifdef RANDOMROI - //randomize ROI - cv::RNG &rng = TS::ptr()->get_rng(); - roicols = rng.uniform(1, mat.cols); - roirows = rng.uniform(1, mat.rows); - srcx = rng.uniform(0, mat.cols - roicols); - srcy = rng.uniform(0, mat.rows - roirows); - dstx = rng.uniform(0, dst.cols - roicols); - dsty = rng.uniform(0, dst.rows - roirows); - maskx = rng.uniform(0, mask.cols - roicols); - masky = rng.uniform(0, mask.rows - roirows); -#else - roicols = mat.cols; - roirows = mat.rows; - srcx = 0; - srcy = 0; - dstx = 0; - dsty = 0; - maskx = 0; - masky = 0; -#endif + if (use_roi) + { + // randomize ROI + cv::RNG &rng = TS::ptr()->get_rng(); + roicols = rng.uniform(1, mat.cols); + roirows = rng.uniform(1, mat.rows); + srcx = rng.uniform(0, mat.cols - roicols); + srcy = rng.uniform(0, mat.rows - roirows); + dstx = rng.uniform(0, dst.cols - roicols); + dsty = rng.uniform(0, dst.rows - roirows); + maskx = rng.uniform(0, mask.cols - roicols); + masky = rng.uniform(0, mask.rows - roirows); + } + else + { + roicols = mat.cols; + roirows = mat.rows; + srcx = srcy = 0; + dstx = dsty = 0; + maskx = masky = 0; + } mat_roi = mat(Rect(srcx, srcy, roicols, roirows)); mask_roi = mask(Rect(maskx, masky, roicols, roirows)); - dst_roi = dst(Rect(dstx, dsty, roicols, roirows)); + dst_roi = dst(Rect(dstx, dsty, roicols, roirows)); gdst_whole = dst; gdst = gdst_whole(Rect(dstx, dsty, roicols, roirows)); @@ -226,11 +228,11 @@ PARAM_TEST_CASE(CopyToTestBase, MatType, bool) } }; -struct CopyTo : CopyToTestBase {}; +typedef CopyToTestBase CopyTo; TEST_P(CopyTo, Without_mask) { - for(int j = 0; j < LOOP_TIMES; j++) + for (int j = 0; j < LOOP_TIMES; j++) { random_roi(); @@ -243,7 +245,7 @@ TEST_P(CopyTo, Without_mask) TEST_P(CopyTo, With_mask) { - for(int j = 0; j < LOOP_TIMES; j++) + for (int j = 0; j < LOOP_TIMES; j++) { random_roi(); @@ -254,14 +256,13 @@ TEST_P(CopyTo, With_mask) } } - - - -///////////////////////////////////////////copyto///////////////////////////////////////////////////////////// +/////////////////////////////////////////// setTo ///////////////////////////////////////////////////////////// PARAM_TEST_CASE(SetToTestBase, MatType, bool) { int type; + bool use_roi; + cv::Scalar val; cv::Mat mat; @@ -275,20 +276,21 @@ PARAM_TEST_CASE(SetToTestBase, MatType, bool) int maskx; int masky; - //src mat with roi + // src mat with roi cv::Mat mat_roi; cv::Mat mask_roi; - //ocl dst mat for testing + // ocl dst mat for testing cv::ocl::oclMat gmat_whole; - //ocl mat with roi + // ocl mat with roi cv::ocl::oclMat gmat; cv::ocl::oclMat gmask; virtual void SetUp() { type = GET_PARAM(0); + use_roi = GET_PARAM(1); cv::RNG &rng = TS::ptr()->get_rng(); cv::Size size(MWIDTH, MHEIGHT); @@ -298,28 +300,28 @@ PARAM_TEST_CASE(SetToTestBase, MatType, bool) cv::threshold(mask, mask, 0.5, 255., CV_8UC1); val = cv::Scalar(rng.uniform(-10.0, 10.0), rng.uniform(-10.0, 10.0), rng.uniform(-10.0, 10.0), rng.uniform(-10.0, 10.0)); - } void random_roi() { -#ifdef RANDOMROI - //randomize ROI - cv::RNG &rng = TS::ptr()->get_rng(); - roicols = rng.uniform(1, mat.cols); - roirows = rng.uniform(1, mat.rows); - srcx = rng.uniform(0, mat.cols - roicols); - srcy = rng.uniform(0, mat.rows - roirows); - maskx = rng.uniform(0, mask.cols - roicols); - masky = rng.uniform(0, mask.rows - roirows); -#else - roicols = mat.cols; - roirows = mat.rows; - srcx = 0; - srcy = 0; - maskx = 0; - masky = 0; -#endif + if (use_roi) + { + // randomize ROI + cv::RNG &rng = TS::ptr()->get_rng(); + roicols = rng.uniform(1, mat.cols); + roirows = rng.uniform(1, mat.rows); + srcx = rng.uniform(0, mat.cols - roicols); + srcy = rng.uniform(0, mat.rows - roirows); + maskx = rng.uniform(0, mask.cols - roicols); + masky = rng.uniform(0, mask.rows - roirows); + } + else + { + roicols = mat.cols; + roirows = mat.rows; + srcx = srcy = 0; + maskx = masky = 0; + } mat_roi = mat(Rect(srcx, srcy, roicols, roirows)); mask_roi = mask(Rect(maskx, masky, roicols, roirows)); @@ -331,11 +333,11 @@ PARAM_TEST_CASE(SetToTestBase, MatType, bool) } }; -struct SetTo : SetToTestBase {}; +typedef SetToTestBase SetTo; TEST_P(SetTo, Without_mask) { - for(int j = 0; j < LOOP_TIMES; j++) + for (int j = 0; j < LOOP_TIMES; j++) { random_roi(); @@ -348,7 +350,7 @@ TEST_P(SetTo, Without_mask) TEST_P(SetTo, With_mask) { - for(int j = 0; j < LOOP_TIMES; j++) + for (int j = 0; j < LOOP_TIMES; j++) { random_roi(); @@ -359,105 +361,84 @@ TEST_P(SetTo, With_mask) } } -//convertC3C4 -PARAM_TEST_CASE(convertC3C4, MatType, cv::Size) +// convertC3C4 + +PARAM_TEST_CASE(convertC3C4, MatType, bool) { - int type; - cv::Size ksize; + int depth; + bool use_roi; //src mat - cv::Mat mat1; - cv::Mat dst; + cv::Mat src; // set up roi - int roicols; - int roirows; - int src1x; - int src1y; - int dstx; - int dsty; + int roicols, roirows; + int srcx, srcy; //src mat with roi - cv::Mat mat1_roi; - cv::Mat dst_roi; - - //ocl dst mat for testing - cv::ocl::oclMat gdst_whole; + cv::Mat src_roi; //ocl mat with roi - cv::ocl::oclMat gmat1; - cv::ocl::oclMat gdst; + cv::ocl::oclMat gsrc_roi; virtual void SetUp() { - type = GET_PARAM(0); - ksize = GET_PARAM(1); + depth = GET_PARAM(0); + use_roi = GET_PARAM(1); + int type = CV_MAKE_TYPE(depth, 3); + cv::RNG &rng = TS::ptr()->get_rng(); + src = randomMat(rng, randomSize(MIN_VALUE, MAX_VALUE), type, 0, 40, false); } void random_roi() { -#ifdef RANDOMROI - //randomize ROI - cv::RNG &rng = TS::ptr()->get_rng(); - roicols = rng.uniform(2, mat1.cols); - roirows = rng.uniform(2, mat1.rows); - src1x = rng.uniform(0, mat1.cols - roicols); - src1y = rng.uniform(0, mat1.rows - roirows); - dstx = rng.uniform(0, dst.cols - roicols); - dsty = rng.uniform(0, dst.rows - roirows); -#else - roicols = mat1.cols; - roirows = mat1.rows; - src1x = 0; - src1y = 0; - dstx = 0; - dsty = 0; -#endif - - mat1_roi = mat1(Rect(src1x, src1y, roicols, roirows)); - dst_roi = dst(Rect(dstx, dsty, roicols, roirows)); - - gdst_whole = dst; - gdst = gdst_whole(Rect(dstx, dsty, roicols, roirows)); - - - gmat1 = mat1_roi; + if (use_roi) + { + //randomize ROI + cv::RNG &rng = TS::ptr()->get_rng(); + roicols = rng.uniform(1, src.cols); + roirows = rng.uniform(1, src.rows); + srcx = rng.uniform(0, src.cols - roicols); + srcy = rng.uniform(0, src.rows - roirows); + } + else + { + roicols = src.cols; + roirows = src.rows; + srcx = srcy = 0; + } + + src_roi = src(Rect(srcx, srcy, roicols, roirows)); } - }; TEST_P(convertC3C4, Accuracy) { - cv::RNG &rng = TS::ptr()->get_rng(); - for(int j = 0; j < LOOP_TIMES; j++) + for (int j = 0; j < LOOP_TIMES; j++) { - //random_roi(); - int width = rng.uniform(2, MWIDTH); - int height = rng.uniform(2, MHEIGHT); - cv::Size size(width, height); + random_roi(); - mat1 = randomMat(rng, size, type, 0, 40, false); - gmat1 = mat1; + gsrc_roi = src_roi; - EXPECT_MAT_NEAR(mat1, Mat(gmat1), 0.0); + EXPECT_MAT_NEAR(src_roi, Mat(gsrc_roi), 0.0); } - } INSTANTIATE_TEST_CASE_P(MatrixOperation, ConvertTo, Combine( - Values(CV_8UC1, CV_8UC3, CV_8UC4, CV_32SC1, CV_32SC4, CV_32FC1, CV_32FC4), - Values(CV_8UC1, CV_8UC3, CV_8UC4, CV_32SC1, CV_32SC4, CV_32FC1, CV_32FC4))); + Values(CV_8U, CV_8S, CV_16U, CV_16S, CV_32S, CV_32F, CV_64F), + Values(CV_8U, CV_8S, CV_16U, CV_16S, CV_32S, CV_32F, CV_64F), + Range(1, 5), Bool())); INSTANTIATE_TEST_CASE_P(MatrixOperation, CopyTo, Combine( Values(CV_8UC1, CV_8UC3, CV_8UC4, CV_32SC1, CV_32SC3, CV_32SC4, CV_32FC1, CV_32FC3, CV_32FC4), - Values(false))); // Values(false) is the reserved parameter + Bool())); INSTANTIATE_TEST_CASE_P(MatrixOperation, SetTo, Combine( Values(CV_8UC1, CV_8UC3, CV_8UC4, CV_32SC1, CV_32SC3, CV_32SC4, CV_32FC1, CV_32FC3, CV_32FC4), - Values(false))); // Values(false) is the reserved parameter + Bool())); INSTANTIATE_TEST_CASE_P(MatrixOperation, convertC3C4, Combine( - Values(CV_8UC3, CV_32SC3, CV_32FC3), - Values(cv::Size()))); + Values(CV_8U, CV_8S, CV_16U, CV_16S, CV_32S, CV_32F, CV_64F), + Bool())); #endif diff --git a/modules/ocl/test/utility.hpp b/modules/ocl/test/utility.hpp index 1e17c6dbca..48c8bbcd9b 100644 --- a/modules/ocl/test/utility.hpp +++ b/modules/ocl/test/utility.hpp @@ -41,9 +41,15 @@ #ifndef __OPENCV_TEST_UTILITY_HPP__ #define __OPENCV_TEST_UTILITY_HPP__ + #define LOOP_TIMES 1 + #define MWIDTH 256 #define MHEIGHT 256 + +#define MIN_VALUE 171 +#define MAX_VALUE 351 + //#define RANDOMROI int randomInt(int minVal, int maxVal); double randomDouble(double minVal, double maxVal); @@ -73,6 +79,7 @@ double checkSimilarity(const cv::Mat &m1, const cv::Mat &m2); //oclMat create cv::ocl::oclMat createMat_ocl(cv::Size size, int type, bool useRoi = false); cv::ocl::oclMat loadMat_ocl(const cv::Mat& m, bool useRoi = false); + #define EXPECT_MAT_NORM(mat, eps) \ { \ EXPECT_LE(checkNorm(cv::Mat(mat)), eps) \ @@ -84,14 +91,7 @@ cv::ocl::oclMat loadMat_ocl(const cv::Mat& m, bool useRoi = false); ASSERT_EQ(mat1.size(), mat2.size()); \ EXPECT_LE(checkNorm(cv::Mat(mat1), cv::Mat(mat2)), eps); \ } -/* -#define EXPECT_MAT_NEAR(mat1, mat2, eps,s) \ -{ \ - ASSERT_EQ(mat1.type(), mat2.type()); \ - ASSERT_EQ(mat1.size(), mat2.size()); \ - EXPECT_LE(checkNorm(cv::Mat(mat1), cv::Mat(mat2)), eps)< Date: Thu, 12 Sep 2013 12:58:10 +0400 Subject: [PATCH 20/68] Revert "Merge pull request #1376 from StevenPuttemans:bugfix_3186" This reverts commit 41b8479d039bb0ecc36627ae50349b6fcd5e16e3, reversing changes made to 531471b0aa4b4e8f9fcab8282d5cbe895b77e6e0. Reason: breaks the build. --- modules/highgui/src/cap_v4l.cpp | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/modules/highgui/src/cap_v4l.cpp b/modules/highgui/src/cap_v4l.cpp index 49432c984a..045c6f889c 100644 --- a/modules/highgui/src/cap_v4l.cpp +++ b/modules/highgui/src/cap_v4l.cpp @@ -1236,10 +1236,10 @@ static int read_frame_v4l2(CvCaptureCAM_V4L* capture) { //set timestamp in capture struct to be timestamp of most recent frame capture->timestamp = buf.timestamp; - return 2; + return 1; } -static int mainloop_v4l2(CvCaptureCAM_V4L* capture) { +static void mainloop_v4l2(CvCaptureCAM_V4L* capture) { unsigned int count; count = 1; @@ -1273,13 +1273,8 @@ static int mainloop_v4l2(CvCaptureCAM_V4L* capture) { break; } - int readresult = read_frame_v412(capture); - if (readresult == 2){ - return 0; - } - if (readresult){ - return 1; - } + if (read_frame_v4l2 (capture)) + break; } } } @@ -1359,10 +1354,7 @@ static int icvGrabFrameCAM_V4L(CvCaptureCAM_V4L* capture) { { // skip first frame. it is often bad -- this is unnotied in traditional apps, // but could be fatal if bad jpeg is enabled - if(!mainloop_v4l2(capture)){ - fprintf( stderr, "HIGHGUI ERROR: V4L: Could not capture image.\n"); - return 0; - } + mainloop_v4l2(capture); } #endif @@ -1374,10 +1366,9 @@ static int icvGrabFrameCAM_V4L(CvCaptureCAM_V4L* capture) { if (V4L2_SUPPORT == 1) { - if(!mainloop_v4l2(capture)){ - fprintf( stderr, "HIGHGUI ERROR: V4L: Could not capture image.\n"); - return 0; - } + + mainloop_v4l2(capture); + } #endif /* HAVE_CAMV4L2 */ #if defined(HAVE_CAMV4L) && defined(HAVE_CAMV4L2) From 9311f841092065daa0df2b63c2f2047a78a6c552 Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Fri, 13 Sep 2013 12:47:25 +0400 Subject: [PATCH 21/68] disabled MOG tests if no video input support --- modules/ocl/perf/perf_bgfg.cpp | 31 +++++++++++++++++++------------ modules/ocl/test/test_bgfg.cpp | 17 ++++++++++++++++- 2 files changed, 35 insertions(+), 13 deletions(-) diff --git a/modules/ocl/perf/perf_bgfg.cpp b/modules/ocl/perf/perf_bgfg.cpp index ecf1127024..cd84509bc0 100644 --- a/modules/ocl/perf/perf_bgfg.cpp +++ b/modules/ocl/perf/perf_bgfg.cpp @@ -44,12 +44,14 @@ // //M*/ #include "perf_precomp.hpp" + using namespace perf; using namespace std; using namespace cv::ocl; using namespace cv; using std::tr1::tuple; using std::tr1::get; + #if defined(HAVE_XINE) || \ defined(HAVE_GSTREAMER) || \ defined(HAVE_QUICKTIME) || \ @@ -63,6 +65,7 @@ using std::tr1::get; #endif #if BUILD_WITH_VIDEO_INPUT_SUPPORT + static void cvtFrameFmt(vector& input, vector& output) { for(int i = 0; i< (int)(input.size()); i++) @@ -70,6 +73,7 @@ static void cvtFrameFmt(vector& input, vector& output) cvtColor(input[i], output[i], COLOR_RGB2GRAY); } } + //prepare data for CPU static void prepareData(VideoCapture& cap, int cn, vector& frame_buffer) { @@ -88,15 +92,15 @@ static void prepareData(VideoCapture& cap, int cn, vector& frame_buffer) else frame_buffer = frame_buffer_init; } + //copy CPU data to GPU static void prepareData(vector& frame_buffer, vector& frame_buffer_ocl) { for(int i = 0; i < (int)frame_buffer.size(); i++) frame_buffer_ocl.push_back(cv::ocl::oclMat(frame_buffer[i])); } -#endif + ///////////// MOG //////////////////////// -#if BUILD_WITH_VIDEO_INPUT_SUPPORT typedef tuple VideoMOGParamType; typedef TestBaseWithParam VideoMOGFixture; @@ -137,7 +141,8 @@ PERF_TEST_P(VideoMOGFixture, MOG, } } SANITY_CHECK(foreground); - }else if(RUN_OCL_IMPL) + } + else if(RUN_OCL_IMPL) { prepareData(frame_buffer, frame_buffer_ocl); CV_Assert((int)(frame_buffer_ocl.size()) == nFrame); @@ -152,13 +157,12 @@ PERF_TEST_P(VideoMOGFixture, MOG, } foreground_d.download(foreground); SANITY_CHECK(foreground); - }else + } + else OCL_PERF_ELSE } -#endif ///////////// MOG2 //////////////////////// -#if BUILD_WITH_VIDEO_INPUT_SUPPORT typedef tuple VideoMOG2ParamType; typedef TestBaseWithParam VideoMOG2Fixture; @@ -196,7 +200,8 @@ PERF_TEST_P(VideoMOG2Fixture, MOG2, } } SANITY_CHECK(foreground); - }else if(RUN_OCL_IMPL) + } + else if(RUN_OCL_IMPL) { prepareData(frame_buffer, frame_buffer_ocl); CV_Assert((int)(frame_buffer_ocl.size()) == nFrame); @@ -211,13 +216,12 @@ PERF_TEST_P(VideoMOG2Fixture, MOG2, } foreground_d.download(foreground); SANITY_CHECK(foreground); - }else + } + else OCL_PERF_ELSE } -#endif ///////////// MOG2_GetBackgroundImage ////////////////// -#if BUILD_WITH_VIDEO_INPUT_SUPPORT typedef TestBaseWithParam Video_MOG2GetBackgroundImage; @@ -259,7 +263,8 @@ PERF_TEST_P(Video_MOG2GetBackgroundImage, MOG2, mog2.getBackgroundImage(background); } SANITY_CHECK(background); - }else if(RUN_OCL_IMPL) + } + else if(RUN_OCL_IMPL) { prepareData(frame_buffer, frame_buffer_ocl); CV_Assert((int)(frame_buffer_ocl.size()) == nFrame); @@ -276,7 +281,9 @@ PERF_TEST_P(Video_MOG2GetBackgroundImage, MOG2, } background_d.download(background); SANITY_CHECK(background); - }else + } + else OCL_PERF_ELSE } + #endif diff --git a/modules/ocl/test/test_bgfg.cpp b/modules/ocl/test/test_bgfg.cpp index bdf3841bef..46d90c80fa 100644 --- a/modules/ocl/test/test_bgfg.cpp +++ b/modules/ocl/test/test_bgfg.cpp @@ -53,7 +53,20 @@ using namespace cvtest; using namespace testing; using namespace std; -extern string workdir; +#if defined(HAVE_XINE) || \ + defined(HAVE_GSTREAMER) || \ + defined(HAVE_QUICKTIME) || \ + defined(HAVE_AVFOUNDATION) || \ + defined(HAVE_FFMPEG) || \ + defined(WIN32) + +# define BUILD_WITH_VIDEO_INPUT_SUPPORT 1 +#else +# define BUILD_WITH_VIDEO_INPUT_SUPPORT 0 +#endif + +#if BUILD_WITH_VIDEO_INPUT_SUPPORT + ////////////////////////////////////////////////////// // MOG @@ -225,3 +238,5 @@ INSTANTIATE_TEST_CASE_P(OCL_Video, mog2, testing::Combine( Values(true, false))); #endif + +#endif From 199aa809156d651b9a225a1bcff4edaea5fac398 Mon Sep 17 00:00:00 2001 From: StevenPuttemans Date: Fri, 13 Sep 2013 15:01:18 +0200 Subject: [PATCH 22/68] Added typo in docs of tutorial --- .../mat_the_basic_image_container.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/tutorials/core/mat_the_basic_image_container/mat_the_basic_image_container.rst b/doc/tutorials/core/mat_the_basic_image_container/mat_the_basic_image_container.rst index 76c5a4541c..171d2e683f 100644 --- a/doc/tutorials/core/mat_the_basic_image_container/mat_the_basic_image_container.rst +++ b/doc/tutorials/core/mat_the_basic_image_container/mat_the_basic_image_container.rst @@ -21,7 +21,7 @@ OpenCV has been around since 2001. In those days the library was built around a Luckily C++ came around and introduced the concept of classes making easier for the user through automatic memory management (more or less). The good news is that C++ is fully compatible with C so no compatibility issues can arise from making the change. Therefore, OpenCV 2.0 introduced a new C++ interface which offered a new way of doing things which means you do not need to fiddle with memory management, making your code concise (less to write, to achieve more). The main downside of the C++ interface is that many embedded development systems at the moment support only C. Therefore, unless you are targeting embedded platforms, there's no point to using the *old* methods (unless you're a masochist programmer and you're asking for trouble). -The first thing you need to know about *Mat* is that you no longer need to manually allocate its memory and release it as soon as you do not need it. While doing this is still a possibility, most of the OpenCV functions will allocate its output data manually. As a nice bonus if you pass on an already existing *Mat* object, which has already allocated the required space for the matrix, this will be reused. In other words we use at all times only as much memory as we need to perform the task. +The first thing you need to know about *Mat* is that you no longer need to manually allocate its memory and release it as soon as you do not need it. While doing this is still a possibility, most of the OpenCV functions will allocate its output data automatically. As a nice bonus if you pass on an already existing *Mat* object, which has already allocated the required space for the matrix, this will be reused. In other words we use at all times only as much memory as we need to perform the task. *Mat* is basically a class with two data parts: the matrix header (containing information such as the size of the matrix, the method used for storing, at which address is the matrix stored, and so on) and a pointer to the matrix containing the pixel values (taking any dimensionality depending on the method chosen for storing) . The matrix header size is constant, however the size of the matrix itself may vary from image to image and usually is larger by orders of magnitude. From 671e5e39b1783d696bc80d979f63611cf0b20ffc Mon Sep 17 00:00:00 2001 From: Roman Donchenko Date: Fri, 13 Sep 2013 17:25:21 +0400 Subject: [PATCH 23/68] Dropped the HAVE_DC1394_095 configuration macro. We never set it, and libdc1394 0.9.5 is obsolete anyway - 1.0 came out in 2004. Note that 1.0 doesn't have the do_extra_buffering parameter. --- cmake/templates/cvconfig.h.cmake | 3 --- modules/highgui/src/cap_dc1394.cpp | 18 +++--------------- 2 files changed, 3 insertions(+), 18 deletions(-) diff --git a/cmake/templates/cvconfig.h.cmake b/cmake/templates/cvconfig.h.cmake index bce8eafcaa..960becf381 100644 --- a/cmake/templates/cvconfig.h.cmake +++ b/cmake/templates/cvconfig.h.cmake @@ -49,9 +49,6 @@ /* IEEE1394 capturing support */ #cmakedefine HAVE_DC1394 -/* libdc1394 0.9.4 or 0.9.5 */ -#cmakedefine HAVE_DC1394_095 - /* IEEE1394 capturing support - libdc1394 v2.x */ #cmakedefine HAVE_DC1394_2 diff --git a/modules/highgui/src/cap_dc1394.cpp b/modules/highgui/src/cap_dc1394.cpp index 3e628acb70..9706bd12e3 100644 --- a/modules/highgui/src/cap_dc1394.cpp +++ b/modules/highgui/src/cap_dc1394.cpp @@ -296,11 +296,7 @@ static CvCaptureCAM_DC1394 * icvCaptureFromCAM_DC1394 (int index) if (pcap->format!=FORMAT_SCALABLE_IMAGE_SIZE) { // everything except Format 7 if (dc1394_dma_setup_capture(pcap->handle, pcap->camera->node, index+1 /*channel*/, pcap->format, pcap->mode, SPEED_400, - pcap->frame_rate, NUM_BUFFERS, -#ifdef HAVE_DC1394_095 - 0 /*do_extra_buffering*/, -#endif - 1 /*DROP_FRAMES*/, + pcap->frame_rate, NUM_BUFFERS, 1 /*drop_frames*/, pcap->device_name, pcap->camera) != DC1394_SUCCESS) { fprintf(stderr,"%s:%d: Failed to setup DMA capture with VIDEO1394\n",__FILE__,__LINE__); goto ERROR; @@ -311,11 +307,7 @@ static CvCaptureCAM_DC1394 * icvCaptureFromCAM_DC1394 (int index) pcap->mode, SPEED_400, QUERY_FROM_CAMERA, (unsigned int)QUERY_FROM_CAMERA, (unsigned int)QUERY_FROM_CAMERA, (unsigned int)QUERY_FROM_CAMERA, (unsigned int)QUERY_FROM_CAMERA, - NUM_BUFFERS, -#ifdef HAVE_DC1394_095 - 0 /*do_extra_buffering*/, -#endif - 1 /*DROP_FRAMES*/, + NUM_BUFFERS, 1 /*drop_frames*/, pcap->device_name, pcap->camera) != DC1394_SUCCESS) { fprintf(stderr,"%s:%d: Failed to setup DMA capture with VIDEO1394\n",__FILE__,__LINE__); goto ERROR; @@ -661,11 +653,7 @@ icvSetModeCAM_DC1394( CvCaptureCAM_DC1394 * capture, int mode ){ dc1394_dma_unlisten(capture->handle, capture->camera); if (dc1394_dma_setup_capture(capture->handle, capture->camera->node, capture->camera->channel /*channel*/, format, mode, SPEED_400, - frame_rate, NUM_BUFFERS, -#ifdef HAVE_DC1394_095 - 0 /*do_extra_buffering*/, -#endif - 1 /*DROP_FRAMES*/, + frame_rate, NUM_BUFFERS, 1 /*drop_frames*/, capture->device_name, capture->camera) != DC1394_SUCCESS) { fprintf(stderr,"%s:%d: Failed to setup DMA capture with VIDEO1394\n",__FILE__,__LINE__); return 0; From 0ee47502d9dcc326201d660b7d00085b43dc8ee1 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Sun, 15 Sep 2013 07:05:30 +0400 Subject: [PATCH 24/68] remove CV_EXPORT from templates --- modules/core/include/opencv2/core/core.hpp | 66 +++++++++---------- .../core/include/opencv2/core/operations.hpp | 42 ++++++------ .../include/opencv2/features2d/features2d.hpp | 2 +- 3 files changed, 55 insertions(+), 55 deletions(-) diff --git a/modules/core/include/opencv2/core/core.hpp b/modules/core/include/opencv2/core/core.hpp index bc1a68fb77..9996c242ad 100644 --- a/modules/core/include/opencv2/core/core.hpp +++ b/modules/core/include/opencv2/core/core.hpp @@ -78,11 +78,11 @@ using std::vector; using std::string; using std::ptrdiff_t; -template class CV_EXPORTS Size_; -template class CV_EXPORTS Point_; -template class CV_EXPORTS Rect_; -template class CV_EXPORTS Vec; -template class CV_EXPORTS Matx; +template class Size_; +template class Point_; +template class Rect_; +template class Vec; +template class Matx; typedef std::string String; @@ -112,10 +112,10 @@ class CV_EXPORTS MatOp_Base; class CV_EXPORTS MatArg; class CV_EXPORTS MatConstIterator; -template class CV_EXPORTS Mat_; -template class CV_EXPORTS MatIterator_; -template class CV_EXPORTS MatConstIterator_; -template class CV_EXPORTS MatCommaInitializer_; +template class Mat_; +template class MatIterator_; +template class MatConstIterator_; +template class MatCommaInitializer_; #if !defined(ANDROID) || (defined(_GLIBCXX_USE_WCHAR_T) && _GLIBCXX_USE_WCHAR_T) typedef std::basic_string WString; @@ -363,7 +363,7 @@ CV_EXPORTS_W bool useOptimized(); /*! The STL-compilant memory Allocator based on cv::fastMalloc() and cv::fastFree() */ -template class CV_EXPORTS Allocator +template class Allocator { public: typedef _Tp value_type; @@ -405,7 +405,7 @@ public: The class is specialized for each fundamental numerical data type supported by OpenCV. It provides DataDepth::value constant. */ -template class CV_EXPORTS DataDepth {}; +template class DataDepth {}; template<> class DataDepth { public: enum { value = CV_8U, fmt=(int)'u' }; }; template<> class DataDepth { public: enum { value = CV_8U, fmt=(int)'u' }; }; @@ -446,7 +446,7 @@ struct CV_EXPORTS Matx_MulOp {}; struct CV_EXPORTS Matx_MatMulOp {}; struct CV_EXPORTS Matx_TOp {}; -template class CV_EXPORTS Matx +template class Matx { public: typedef _Tp value_type; @@ -595,7 +595,7 @@ typedef Matx Matx66d; In addition to the universal notation like Vec, you can use shorter aliases for the most popular specialized variants of Vec, e.g. Vec3f ~ Vec. */ -template class CV_EXPORTS Vec : public Matx<_Tp, cn, 1> +template class Vec : public Matx<_Tp, cn, 1> { public: typedef _Tp value_type; @@ -691,7 +691,7 @@ typedef Vec Vec6d; more convenient access to the real and imaginary parts using through the simple field access, as opposite to std::complex::real() and std::complex::imag(). */ -template class CV_EXPORTS Complex +template class Complex { public: @@ -727,7 +727,7 @@ typedef Complex Complexd; as a template parameter. There are a few shorter aliases available for user convenience. See cv::Point, cv::Point2i, cv::Point2f and cv::Point2d. */ -template class CV_EXPORTS Point_ +template class Point_ { public: typedef _Tp value_type; @@ -770,7 +770,7 @@ public: \see cv::Point3i, cv::Point3f and cv::Point3d */ -template class CV_EXPORTS Point3_ +template class Point3_ { public: typedef _Tp value_type; @@ -809,7 +809,7 @@ public: The class represents the size of a 2D rectangle, image size, matrix size etc. Normally, cv::Size ~ cv::Size_ is used. */ -template class CV_EXPORTS Size_ +template class Size_ { public: typedef _Tp value_type; @@ -844,7 +844,7 @@ public: The class represents a 2D rectangle with coordinates of the specified data type. Normally, cv::Rect ~ cv::Rect_ is used. */ -template class CV_EXPORTS Rect_ +template class Rect_ { public: typedef _Tp value_type; @@ -933,7 +933,7 @@ public: This is partially specialized cv::Vec class with the number of elements = 4, i.e. a short vector of four elements. Normally, cv::Scalar ~ cv::Scalar_ is used. */ -template class CV_EXPORTS Scalar_ : public Vec<_Tp, 4> +template class Scalar_ : public Vec<_Tp, 4> { public: //! various constructors @@ -1260,7 +1260,7 @@ public: \note{Another good property of the class is that the operations on the reference counter are atomic, i.e. it is safe to use the class in multi-threaded applications} */ -template class CV_EXPORTS Ptr +template class Ptr { public: //! empty constructor @@ -2748,7 +2748,7 @@ CV_EXPORTS_W Size getTextSize(const string& text, int fontFace, img(i,j)[2] ^= (uchar)(i ^ j); // img(y,x)[c] accesses c-th channel of the pixel (x,y) \endcode */ -template class CV_EXPORTS Mat_ : public Mat +template class Mat_ : public Mat { public: typedef _Tp value_type; @@ -2980,7 +2980,7 @@ public: */ template -class CV_EXPORTS MatConstIterator_ : public MatConstIterator +class MatConstIterator_ : public MatConstIterator { public: typedef _Tp value_type; @@ -3031,7 +3031,7 @@ public: */ template -class CV_EXPORTS MatIterator_ : public MatConstIterator_<_Tp> +class MatIterator_ : public MatConstIterator_<_Tp> { public: typedef _Tp* pointer; @@ -3072,7 +3072,7 @@ public: MatIterator_ operator ++(int); }; -template class CV_EXPORTS MatOp_Iter_; +template class MatOp_Iter_; /*! Comma-separated Matrix Initializer @@ -3087,7 +3087,7 @@ template class CV_EXPORTS MatOp_Iter_; Mat R = (Mat_(2,2) << a, -b, b, a); \endcode */ -template class CV_EXPORTS MatCommaInitializer_ +template class MatCommaInitializer_ { public: //! the constructor, created by "matrix << firstValue" operator, where matrix is cv::Mat @@ -3102,7 +3102,7 @@ protected: }; -template class CV_EXPORTS MatxCommaInitializer +template class MatxCommaInitializer { public: MatxCommaInitializer(Matx<_Tp, m, n>* _mtx); @@ -3113,7 +3113,7 @@ public: int idx; }; -template class CV_EXPORTS VecCommaInitializer : public MatxCommaInitializer<_Tp, m, 1> +template class VecCommaInitializer : public MatxCommaInitializer<_Tp, m, 1> { public: VecCommaInitializer(Vec<_Tp, m>* _vec); @@ -3148,7 +3148,7 @@ public: } \endcode */ -template class CV_EXPORTS AutoBuffer +template class AutoBuffer { public: typedef _Tp value_type; @@ -3719,7 +3719,7 @@ public: m_.ref(2) += m_(3); // equivalent to m.ref(2) += m.value(3); \endcode */ -template class CV_EXPORTS SparseMat_ : public SparseMat +template class SparseMat_ : public SparseMat { public: typedef SparseMatIterator_<_Tp> iterator; @@ -3793,7 +3793,7 @@ public: This is the derived from SparseMatConstIterator class that introduces more convenient operator *() for accessing the current element. */ -template class CV_EXPORTS SparseMatConstIterator_ : public SparseMatConstIterator +template class SparseMatConstIterator_ : public SparseMatConstIterator { public: typedef std::forward_iterator_tag iterator_category; @@ -3823,7 +3823,7 @@ public: This is the derived from cv::SparseMatConstIterator_ class that introduces more convenient operator *() for accessing the current element. */ -template class CV_EXPORTS SparseMatIterator_ : public SparseMatConstIterator_<_Tp> +template class SparseMatIterator_ : public SparseMatConstIterator_<_Tp> { public: typedef std::forward_iterator_tag iterator_category; @@ -4255,7 +4255,7 @@ typedef Ptr MemStorage; i.e. no constructors or destructors are called for the sequence elements. */ -template class CV_EXPORTS Seq +template class Seq { public: typedef SeqIterator<_Tp> iterator; @@ -4338,7 +4338,7 @@ public: /*! STL-style Sequence Iterator inherited from the CvSeqReader structure */ -template class CV_EXPORTS SeqIterator : public CvSeqReader +template class SeqIterator : public CvSeqReader { public: //! the default constructor diff --git a/modules/core/include/opencv2/core/operations.hpp b/modules/core/include/opencv2/core/operations.hpp index e100f754eb..9569d1abc0 100644 --- a/modules/core/include/opencv2/core/operations.hpp +++ b/modules/core/include/opencv2/core/operations.hpp @@ -711,7 +711,7 @@ CV_EXPORTS bool Cholesky(float* A, size_t astep, int m, float* b, size_t bstep, CV_EXPORTS bool Cholesky(double* A, size_t astep, int m, double* b, size_t bstep, int n); -template struct CV_EXPORTS Matx_DetOp +template struct Matx_DetOp { double operator ()(const Matx<_Tp, m, m>& a) const { @@ -726,7 +726,7 @@ template struct CV_EXPORTS Matx_DetOp }; -template struct CV_EXPORTS Matx_DetOp<_Tp, 1> +template struct Matx_DetOp<_Tp, 1> { double operator ()(const Matx<_Tp, 1, 1>& a) const { @@ -735,7 +735,7 @@ template struct CV_EXPORTS Matx_DetOp<_Tp, 1> }; -template struct CV_EXPORTS Matx_DetOp<_Tp, 2> +template struct Matx_DetOp<_Tp, 2> { double operator ()(const Matx<_Tp, 2, 2>& a) const { @@ -744,7 +744,7 @@ template struct CV_EXPORTS Matx_DetOp<_Tp, 2> }; -template struct CV_EXPORTS Matx_DetOp<_Tp, 3> +template struct Matx_DetOp<_Tp, 3> { double operator ()(const Matx<_Tp, 3, 3>& a) const { @@ -778,7 +778,7 @@ Matx<_Tp, n, m> Matx<_Tp, m, n>::t() const } -template struct CV_EXPORTS Matx_FastInvOp +template struct Matx_FastInvOp { bool operator()(const Matx<_Tp, m, m>& a, Matx<_Tp, m, m>& b, int method) const { @@ -796,7 +796,7 @@ template struct CV_EXPORTS Matx_FastInvOp }; -template struct CV_EXPORTS Matx_FastInvOp<_Tp, 2> +template struct Matx_FastInvOp<_Tp, 2> { bool operator()(const Matx<_Tp, 2, 2>& a, Matx<_Tp, 2, 2>& b, int) const { @@ -813,7 +813,7 @@ template struct CV_EXPORTS Matx_FastInvOp<_Tp, 2> }; -template struct CV_EXPORTS Matx_FastInvOp<_Tp, 3> +template struct Matx_FastInvOp<_Tp, 3> { bool operator()(const Matx<_Tp, 3, 3>& a, Matx<_Tp, 3, 3>& b, int) const { @@ -853,7 +853,7 @@ Matx<_Tp, n, m> Matx<_Tp, m, n>::inv(int method) const } -template struct CV_EXPORTS Matx_FastSolveOp +template struct Matx_FastSolveOp { bool operator()(const Matx<_Tp, m, m>& a, const Matx<_Tp, m, n>& b, Matx<_Tp, m, n>& x, int method) const @@ -868,7 +868,7 @@ template struct CV_EXPORTS Matx_FastSolveOp }; -template struct CV_EXPORTS Matx_FastSolveOp<_Tp, 2, 1> +template struct Matx_FastSolveOp<_Tp, 2, 1> { bool operator()(const Matx<_Tp, 2, 2>& a, const Matx<_Tp, 2, 1>& b, Matx<_Tp, 2, 1>& x, int) const @@ -884,7 +884,7 @@ template struct CV_EXPORTS Matx_FastSolveOp<_Tp, 2, 1> }; -template struct CV_EXPORTS Matx_FastSolveOp<_Tp, 3, 1> +template struct Matx_FastSolveOp<_Tp, 3, 1> { bool operator()(const Matx<_Tp, 3, 3>& a, const Matx<_Tp, 3, 1>& b, Matx<_Tp, 3, 1>& x, int) const @@ -2265,7 +2265,7 @@ inline Range::operator CvSlice() const // 1) it can be created on top of user-allocated data w/o copying it // 2) vector b = a means copying the header, // not the underlying data (use clone() to make a deep copy) -template class CV_EXPORTS Vector +template class Vector { public: typedef _Tp value_type; @@ -2274,7 +2274,7 @@ public: typedef _Tp& reference; typedef const _Tp& const_reference; - struct CV_EXPORTS Hdr + struct Hdr { Hdr() : data(0), datastart(0), refcount(0), size(0), capacity(0) {}; _Tp* data; @@ -2858,7 +2858,7 @@ inline void write(FileStorage& fs, const string& name, const Range& r ) write(fs, r.end); } -template class CV_EXPORTS VecWriterProxy +template class VecWriterProxy { public: VecWriterProxy( FileStorage* _fs ) : fs(_fs) {} @@ -2871,7 +2871,7 @@ public: FileStorage* fs; }; -template class CV_EXPORTS VecWriterProxy<_Tp,1> +template class VecWriterProxy<_Tp,1> { public: VecWriterProxy( FileStorage* _fs ) : fs(_fs) {} @@ -3086,7 +3086,7 @@ inline void FileNode::readRaw( const string& fmt, uchar* vec, size_t len ) const begin().readRaw( fmt, vec, len ); } -template class CV_EXPORTS VecReaderProxy +template class VecReaderProxy { public: VecReaderProxy( FileNodeIterator* _it ) : it(_it) {} @@ -3100,7 +3100,7 @@ public: FileNodeIterator* it; }; -template class CV_EXPORTS VecReaderProxy<_Tp,1> +template class VecReaderProxy<_Tp,1> { public: VecReaderProxy( FileNodeIterator* _it ) : it(_it) {} @@ -3414,19 +3414,19 @@ template void sort( vector<_Tp>& vec, _LT LT=_LT() ) } } -template class CV_EXPORTS LessThan +template class LessThan { public: bool operator()(const _Tp& a, const _Tp& b) const { return a < b; } }; -template class CV_EXPORTS GreaterEq +template class GreaterEq { public: bool operator()(const _Tp& a, const _Tp& b) const { return a >= b; } }; -template class CV_EXPORTS LessThanIdx +template class LessThanIdx { public: LessThanIdx( const _Tp* _arr ) : arr(_arr) {} @@ -3434,7 +3434,7 @@ public: const _Tp* arr; }; -template class CV_EXPORTS GreaterEqIdx +template class GreaterEqIdx { public: GreaterEqIdx( const _Tp* _arr ) : arr(_arr) {} @@ -3763,7 +3763,7 @@ template inline bool operator != (const SeqIterator<_Tp>& a, } -template struct CV_EXPORTS RTTIImpl +template struct RTTIImpl { public: static int isInstance(const void* ptr) diff --git a/modules/features2d/include/opencv2/features2d/features2d.hpp b/modules/features2d/include/opencv2/features2d/features2d.hpp index d4649baab9..d598ed6d78 100644 --- a/modules/features2d/include/opencv2/features2d/features2d.hpp +++ b/modules/features2d/include/opencv2/features2d/features2d.hpp @@ -1017,7 +1017,7 @@ struct CV_EXPORTS Hamming typedef Hamming HammingLUT; -template struct CV_EXPORTS HammingMultilevel +template struct HammingMultilevel { enum { normType = NORM_HAMMING + (cellsize>1) }; typedef unsigned char ValueType; From a5c9d83617778e0f858c9f7fa5ae995e29a775ca Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Sun, 15 Sep 2013 19:56:05 +0400 Subject: [PATCH 25/68] fixed ocl::pyrUp for 2-byte types --- modules/ocl/src/opencl/pyr_up.cl | 764 +------------------------------ modules/ocl/src/pyrup.cpp | 20 +- 2 files changed, 33 insertions(+), 751 deletions(-) diff --git a/modules/ocl/src/opencl/pyr_up.cl b/modules/ocl/src/opencl/pyr_up.cl index f58205c02a..88efa9539f 100644 --- a/modules/ocl/src/opencl/pyr_up.cl +++ b/modules/ocl/src/opencl/pyr_up.cl @@ -46,430 +46,25 @@ // //M*/ -uchar get_valid_uchar(float data) -{ - return (uchar)(data <= 255 ? data : data > 0 ? 255 : 0); -} - -/////////////////////////////////////////////////////////////////////// -////////////////////////// CV_8UC1 ////////////////////////////////// -/////////////////////////////////////////////////////////////////////// - -__kernel void pyrUp_C1_D0(__global uchar* src, __global uchar* dst, - int srcRows, int dstRows, int srcCols, int dstCols, - int srcOffset, int dstOffset, int srcStep, int dstStep) -{ - const int x = get_global_id(0); - const int y = get_global_id(1); - __local float s_srcPatch[10][10]; - __local float s_dstPatch[20][16]; - const int tidx = get_local_id(0); - const int tidy = get_local_id(1); - const int lsizex = get_local_size(0); - const int lsizey = get_local_size(1); - - if( tidx < 10 && tidy < 10 ) - { - int srcx = mad24((int)get_group_id(0), (lsizex>>1), tidx) - 1; - int srcy = mad24((int)get_group_id(1), (lsizey>>1), tidy) - 1; - - srcx = abs(srcx); - srcx = min(srcCols - 1,srcx); - - srcy = abs(srcy); - srcy = min(srcRows -1 ,srcy); - - s_srcPatch[tidy][tidx] = (float)(src[srcx + srcy * srcStep]); - - } - - barrier(CLK_LOCAL_MEM_FENCE); - - float sum = 0; - const int evenFlag = (int)((tidx & 1) == 0); - const int oddFlag = (int)((tidx & 1) != 0); - const bool eveny = ((tidy & 1) == 0); - - if(eveny) - { - sum = (evenFlag * 0.0625f) * s_srcPatch[1 + (tidy >> 1)][1 + ((tidx - 2) >> 1)]; - sum = sum + ( oddFlag * 0.25f ) * s_srcPatch[1 + (tidy >> 1)][1 + ((tidx - 1) >> 1)]; - sum = sum + (evenFlag * 0.375f ) * s_srcPatch[1 + (tidy >> 1)][1 + ((tidx ) >> 1)]; - sum = sum + ( oddFlag * 0.25f ) * s_srcPatch[1 + (tidy >> 1)][1 + ((tidx + 1) >> 1)]; - sum = sum + (evenFlag * 0.0625f) * s_srcPatch[1 + (tidy >> 1)][1 + ((tidx + 2) >> 1)]; - } - - s_dstPatch[2 + get_local_id(1)][get_local_id(0)] = sum; - - if (get_local_id(1) < 2) - { - sum = 0; - - if (eveny) - { - sum = (evenFlag * 0.0625f) * s_srcPatch[lsizey - 16][1 + ((tidx - 2) >> 1)]; - sum = sum + ( oddFlag * 0.25f ) * s_srcPatch[lsizey - 16][1 + ((tidx - 1) >> 1)]; - sum = sum + (evenFlag * 0.375f ) * s_srcPatch[lsizey - 16][1 + ((tidx ) >> 1)]; - sum = sum + ( oddFlag * 0.25f ) * s_srcPatch[lsizey - 16][1 + ((tidx + 1) >> 1)]; - sum = sum + (evenFlag * 0.0625f) * s_srcPatch[lsizey - 16][1 + ((tidx + 2) >> 1)]; - } - - s_dstPatch[get_local_id(1)][get_local_id(0)] = sum; - } - - if (get_local_id(1) > 13) - { - sum = 0; - - if (eveny) - { - sum = (evenFlag * 0.0625f) * s_srcPatch[lsizey - 7][1 + ((tidx - 2) >> 1)]; - sum = sum + ( oddFlag * 0.25f ) * s_srcPatch[lsizey - 7][1 + ((tidx - 1) >> 1)]; - sum = sum + (evenFlag * 0.375f ) * s_srcPatch[lsizey - 7][1 + ((tidx ) >> 1)]; - sum = sum + ( oddFlag * 0.25f ) * s_srcPatch[lsizey - 7][1 + ((tidx + 1) >> 1)]; - sum = sum + (evenFlag * 0.0625f) * s_srcPatch[9][1 + ((tidx + 2) >> 1)]; - } - s_dstPatch[4 + tidy][tidx] = sum; - } - - barrier(CLK_LOCAL_MEM_FENCE); - - sum = 0; - - sum = 0.0625f * s_dstPatch[2 + tidy - 2][tidx]; - sum = sum + 0.25f * s_dstPatch[2 + tidy - 1][tidx]; - sum = sum + 0.375f * s_dstPatch[2 + tidy ][tidx]; - sum = sum + 0.25f * s_dstPatch[2 + tidy + 1][tidx]; - sum = sum + 0.0625f * s_dstPatch[2 + tidy + 2][tidx]; - - if ((x < dstCols) && (y < dstRows)) - dst[x + y * dstStep] = convert_uchar_sat_rte(4.0f * sum); -} - -/////////////////////////////////////////////////////////////////////// -////////////////////////// CV_16UC1 ///////////////////////////////// -/////////////////////////////////////////////////////////////////////// - -__kernel void pyrUp_C1_D2(__global ushort* src, __global ushort* dst, - int srcRows, int dstRows, int srcCols, int dstCols, - int srcOffset, int dstOffset, int srcStep, int dstStep) -{ - const int x = get_global_id(0); - const int y = get_global_id(1); - - __local float s_srcPatch[10][10]; - __local float s_dstPatch[20][16]; - - srcStep = srcStep >> 1; - dstStep = dstStep >> 1; - srcOffset = srcOffset >> 1; - dstOffset = dstOffset >> 1; - - - if( get_local_id(0) < 10 && get_local_id(1) < 10 ) - { - int srcx = (int)(get_group_id(0) * get_local_size(0) / 2 + get_local_id(0)) - 1; - int srcy = (int)(get_group_id(1) * get_local_size(1) / 2 + get_local_id(1)) - 1; - - srcx = abs(srcx); - srcx = min(srcCols - 1,srcx); - - srcy = abs(srcy); - srcy = min(srcRows -1 ,srcy); - - s_srcPatch[get_local_id(1)][get_local_id(0)] = (float)(src[srcx + srcy * srcStep]); - - } - - barrier(CLK_LOCAL_MEM_FENCE); - - float sum = 0; - - const int evenFlag = (int)((get_local_id(0) & 1) == 0); - const int oddFlag = (int)((get_local_id(0) & 1) != 0); - const bool eveny = ((get_local_id(1) & 1) == 0); - const int tidx = get_local_id(0); - - if(eveny) - { - sum = sum + (evenFlag * 0.0625f) * s_srcPatch[1 + (get_local_id(1) >> 1)][1 + ((tidx - 2) >> 1)]; - sum = sum + ( oddFlag * 0.25f ) * s_srcPatch[1 + (get_local_id(1) >> 1)][1 + ((tidx - 1) >> 1)]; - sum = sum + (evenFlag * 0.375f ) * s_srcPatch[1 + (get_local_id(1) >> 1)][1 + ((tidx ) >> 1)]; - sum = sum + ( oddFlag * 0.25f ) * s_srcPatch[1 + (get_local_id(1) >> 1)][1 + ((tidx + 1) >> 1)]; - sum = sum + (evenFlag * 0.0625f) * s_srcPatch[1 + (get_local_id(1) >> 1)][1 + ((tidx + 2) >> 1)]; - } - - s_dstPatch[2 + get_local_id(1)][get_local_id(0)] = sum; - - if (get_local_id(1) < 2) - { - sum = 0; - - if (eveny) - { - sum = sum + (evenFlag * 0.0625f) * s_srcPatch[0][1 + ((tidx - 2) >> 1)]; - sum = sum + ( oddFlag * 0.25f ) * s_srcPatch[0][1 + ((tidx - 1) >> 1)]; - sum = sum + (evenFlag * 0.375f ) * s_srcPatch[0][1 + ((tidx ) >> 1)]; - sum = sum + ( oddFlag * 0.25f ) * s_srcPatch[0][1 + ((tidx + 1) >> 1)]; - sum = sum + (evenFlag * 0.0625f) * s_srcPatch[0][1 + ((tidx + 2) >> 1)]; - } - - s_dstPatch[get_local_id(1)][get_local_id(0)] = sum; - } - - if (get_local_id(1) > 13) - { - sum = 0; - - if (eveny) - { - sum = sum + (evenFlag * 0.0625f) * s_srcPatch[9][1 + ((tidx - 2) >> 1)]; - sum = sum + ( oddFlag * 0.25f ) * s_srcPatch[9][1 + ((tidx - 1) >> 1)]; - sum = sum + (evenFlag * 0.375f ) * s_srcPatch[9][1 + ((tidx ) >> 1)]; - sum = sum + ( oddFlag * 0.25f ) * s_srcPatch[9][1 + ((tidx + 1) >> 1)]; - sum = sum + (evenFlag * 0.0625f) * s_srcPatch[9][1 + ((tidx + 2) >> 1)]; - } - s_dstPatch[4 + get_local_id(1)][get_local_id(0)] = sum; - } - - barrier(CLK_LOCAL_MEM_FENCE); - - sum = 0; - - const int tidy = get_local_id(1); - - sum = sum + 0.0625f * s_dstPatch[2 + tidy - 2][get_local_id(0)]; - sum = sum + 0.25f * s_dstPatch[2 + tidy - 1][get_local_id(0)]; - sum = sum + 0.375f * s_dstPatch[2 + tidy ][get_local_id(0)]; - sum = sum + 0.25f * s_dstPatch[2 + tidy + 1][get_local_id(0)]; - sum = sum + 0.0625f * s_dstPatch[2 + tidy + 2][get_local_id(0)]; - - if ((x < dstCols) && (y < dstRows)) - dst[x + y * dstStep] = convert_ushort_sat_rte(4.0f * sum); -} - /////////////////////////////////////////////////////////////////////// -////////////////////////// CV_16SC1 ///////////////////////////////// +//////////////////////// Generic PyrUp ////////////////////////////// /////////////////////////////////////////////////////////////////////// -__kernel void pyrUp_C1_D3(__global short* src, __global short* dst, +__kernel void pyrUp(__global Type* src, __global Type* dst, int srcRows, int dstRows, int srcCols, int dstCols, int srcOffset, int dstOffset, int srcStep, int dstStep) { const int x = get_global_id(0); const int y = get_global_id(1); - __local float s_srcPatch[10][10]; - __local float s_dstPatch[20][16]; - - srcStep = srcStep >> 1; - dstStep = dstStep >> 1; - srcOffset = srcOffset >> 1; - dstOffset = dstOffset >> 1; - - if( get_local_id(0) < 10 && get_local_id(1) < 10 ) - { - int srcx = (int)(get_group_id(0) * get_local_size(0) / 2 + get_local_id(0)) - 1; - int srcy = (int)(get_group_id(1) * get_local_size(1) / 2 + get_local_id(1)) - 1; - - srcx = abs(srcx); - srcx = min(srcCols - 1,srcx); - - srcy = abs(srcy); - srcy = min(srcRows -1 ,srcy); - - s_srcPatch[get_local_id(1)][get_local_id(0)] = (float)(src[srcx + srcy * srcStep]); - } - - barrier(CLK_LOCAL_MEM_FENCE); - - float sum = 0; - - const int evenFlag = (int)((get_local_id(0) & 1) == 0); - const int oddFlag = (int)((get_local_id(0) & 1) != 0); - const bool eveny = ((get_local_id(1) & 1) == 0); - const int tidx = get_local_id(0); - - if (eveny) - { - sum = sum + (evenFlag * 0.0625f) * s_srcPatch[1 + (get_local_id(1) >> 1)][1 + ((tidx - 2) >> 1)]; - sum = sum + ( oddFlag * 0.25f ) * s_srcPatch[1 + (get_local_id(1) >> 1)][1 + ((tidx - 1) >> 1)]; - sum = sum + (evenFlag * 0.375f ) * s_srcPatch[1 + (get_local_id(1) >> 1)][1 + ((tidx ) >> 1)]; - sum = sum + ( oddFlag * 0.25f ) * s_srcPatch[1 + (get_local_id(1) >> 1)][1 + ((tidx + 1) >> 1)]; - sum = sum + (evenFlag * 0.0625f) * s_srcPatch[1 + (get_local_id(1) >> 1)][1 + ((tidx + 2) >> 1)]; - } - - s_dstPatch[2 + get_local_id(1)][get_local_id(0)] = sum; - - if (get_local_id(1) < 2) - { - sum = 0; - - if (eveny) - { - sum = sum + (evenFlag * 0.0625f) * s_srcPatch[0][1 + ((tidx - 2) >> 1)]; - sum = sum + ( oddFlag * 0.25f ) * s_srcPatch[0][1 + ((tidx - 1) >> 1)]; - sum = sum + (evenFlag * 0.375f ) * s_srcPatch[0][1 + ((tidx ) >> 1)]; - sum = sum + ( oddFlag * 0.25f ) * s_srcPatch[0][1 + ((tidx + 1) >> 1)]; - sum = sum + (evenFlag * 0.0625f) * s_srcPatch[0][1 + ((tidx + 2) >> 1)]; - } - - s_dstPatch[get_local_id(1)][get_local_id(0)] = sum; - } - - if (get_local_id(1) > 13) - { - sum = 0; - - if (eveny) - { - sum = sum + (evenFlag * 0.0625f) * s_srcPatch[9][1 + ((tidx - 2) >> 1)]; - sum = sum + ( oddFlag * 0.25f ) * s_srcPatch[9][1 + ((tidx - 1) >> 1)]; - sum = sum + (evenFlag * 0.375f ) * s_srcPatch[9][1 + ((tidx ) >> 1)]; - sum = sum + ( oddFlag * 0.25f ) * s_srcPatch[9][1 + ((tidx + 1) >> 1)]; - sum = sum + (evenFlag * 0.0625f) * s_srcPatch[9][1 + ((tidx + 2) >> 1)]; - } - s_dstPatch[4 + get_local_id(1)][get_local_id(0)] = sum; - } - - barrier(CLK_LOCAL_MEM_FENCE); - - sum = 0; - - const int tidy = get_local_id(1); - - sum = sum + 0.0625f * s_dstPatch[2 + tidy - 2][get_local_id(0)]; - sum = sum + 0.25f * s_dstPatch[2 + tidy - 1][get_local_id(0)]; - sum = sum + 0.375f * s_dstPatch[2 + tidy ][get_local_id(0)]; - sum = sum + 0.25f * s_dstPatch[2 + tidy + 1][get_local_id(0)]; - sum = sum + 0.0625f * s_dstPatch[2 + tidy + 2][get_local_id(0)]; - - if ((x < dstCols) && (y < dstRows)) - dst[x + y * dstStep] = convert_short_sat_rte(4.0f * sum); -} - -/////////////////////////////////////////////////////////////////////// -////////////////////////// CV_32FC1 ///////////////////////////////// -/////////////////////////////////////////////////////////////////////// - -__kernel void pyrUp_C1_D5(__global float* src, __global float* dst, - int srcRows, int dstRows, int srcCols, int dstCols, - int srcOffset, int dstOffset, int srcStep, int dstStep) -{ - const int x = get_global_id(0); - const int y = get_global_id(1); - const int tidx = get_local_id(0); - const int tidy = get_local_id(1); const int lsizex = get_local_size(0); const int lsizey = get_local_size(1); - __local float s_srcPatch[10][10]; - __local float s_dstPatch[20][16]; - - srcOffset = srcOffset >> 2; - dstOffset = dstOffset >> 2; - srcStep = srcStep >> 2; - dstStep = dstStep >> 2; - - - if( tidx < 10 && tidy < 10 ) - { - int srcx = mad24((int)get_group_id(0), lsizex>>1, tidx) - 1; - int srcy = mad24((int)get_group_id(1), lsizey>>1, tidy) - 1; - - srcx = abs(srcx); - srcx = min(srcCols - 1,srcx); - - srcy = abs(srcy); - srcy = min(srcRows -1 ,srcy); - - s_srcPatch[tidy][tidx] = (float)(src[srcx + srcy * srcStep]); - - } - - barrier(CLK_LOCAL_MEM_FENCE); - - float sum = 0; - const int evenFlag = (int)((tidx & 1) == 0); - const int oddFlag = (int)((tidx & 1) != 0); - const bool eveny = ((tidy & 1) == 0); - - - if(eveny) - { - sum = sum + (evenFlag * 0.0625f) * s_srcPatch[1 + (tidy >> 1)][1 + ((tidx - 2) >> 1)]; - sum = sum + ( oddFlag * 0.25f ) * s_srcPatch[1 + (tidy >> 1)][1 + ((tidx - 1) >> 1)]; - sum = sum + (evenFlag * 0.375f ) * s_srcPatch[1 + (tidy >> 1)][1 + ((tidx ) >> 1)]; - sum = sum + ( oddFlag * 0.25f ) * s_srcPatch[1 + (tidy >> 1)][1 + ((tidx + 1) >> 1)]; - sum = sum + (evenFlag * 0.0625f) * s_srcPatch[1 + (tidy >> 1)][1 + ((tidx + 2) >> 1)]; - } - - s_dstPatch[2 + tidy][tidx] = sum; - - if (tidy < 2) - { - sum = 0; - - if (eveny) - { - sum = sum + (evenFlag * 0.0625f) * s_srcPatch[lsizey - 16][1 + ((tidx - 2) >> 1)]; - sum = sum + ( oddFlag * 0.25f ) * s_srcPatch[lsizey - 16][1 + ((tidx - 1) >> 1)]; - sum = sum + (evenFlag * 0.375f ) * s_srcPatch[lsizey - 16][1 + ((tidx ) >> 1)]; - sum = sum + ( oddFlag * 0.25f ) * s_srcPatch[lsizey - 16][1 + ((tidx + 1) >> 1)]; - sum = sum + (evenFlag * 0.0625f) * s_srcPatch[lsizey - 16][1 + ((tidx + 2) >> 1)]; - } - - s_dstPatch[tidy][tidx] = sum; - } - - if (tidy > 13) - { - sum = 0; - - if (eveny) - { - sum = sum + (evenFlag * 0.0625f) * s_srcPatch[lsizey - 7][1 + ((tidx - 2) >> 1)]; - sum = sum + ( oddFlag * 0.25f ) * s_srcPatch[lsizey - 7][1 + ((tidx - 1) >> 1)]; - sum = sum + (evenFlag * 0.375f ) * s_srcPatch[lsizey - 7][1 + ((tidx ) >> 1)]; - sum = sum + ( oddFlag * 0.25f ) * s_srcPatch[lsizey - 7][1 + ((tidx + 1) >> 1)]; - sum = sum + (evenFlag * 0.0625f) * s_srcPatch[lsizey - 7][1 + ((tidx + 2) >> 1)]; - } - s_dstPatch[4 + tidy][tidx] = sum; - } - - barrier(CLK_LOCAL_MEM_FENCE); - - sum = 0.0625f * s_dstPatch[2 + tidy - 2][tidx]; - sum = sum + 0.25f * s_dstPatch[2 + tidy - 1][tidx]; - sum = sum + 0.375f * s_dstPatch[2 + tidy ][tidx]; - sum = sum + 0.25f * s_dstPatch[2 + tidy + 1][tidx]; - sum = sum + 0.0625f * s_dstPatch[2 + tidy + 2][tidx]; - - if ((x < dstCols) && (y < dstRows)) - dst[x + y * dstStep] = (float)(4.0f * sum); -} - -/////////////////////////////////////////////////////////////////////// -////////////////////////// CV_8UC4 ////////////////////////////////// -/////////////////////////////////////////////////////////////////////// -__kernel void pyrUp_C4_D0(__global uchar4* src, __global uchar4* dst, - int srcRows, int dstRows, int srcCols, int dstCols, - int srcOffset, int dstOffset, int srcStep, int dstStep) -{ - const int x = get_global_id(0); - const int y = get_global_id(1); const int tidx = get_local_id(0); const int tidy = get_local_id(1); - const int lsizex = get_local_size(0); - const int lsizey = get_local_size(1); - __local float4 s_srcPatch[10][10]; - __local float4 s_dstPatch[20][16]; - - srcOffset >>= 2; - dstOffset >>= 2; - srcStep >>= 2; - dstStep >>= 2; + __local floatType s_srcPatch[10][10]; + __local floatType s_dstPatch[20][16]; if( tidx < 10 && tidy < 10 ) { @@ -482,351 +77,27 @@ __kernel void pyrUp_C4_D0(__global uchar4* src, __global uchar4* dst, srcy = abs(srcy); srcy = min(srcRows -1 ,srcy); - s_srcPatch[tidy][tidx] = convert_float4(src[srcx + srcy * srcStep]); - } - - barrier(CLK_LOCAL_MEM_FENCE); - - float4 sum = (float4)(0,0,0,0); - - const float4 evenFlag = (float4)((tidx & 1) == 0); - const float4 oddFlag = (float4)((tidx & 1) != 0); - const bool eveny = ((tidy & 1) == 0); - - float4 co1 = (float4)(0.375f, 0.375f, 0.375f, 0.375f); - float4 co2 = (float4)(0.25f, 0.25f, 0.25f, 0.25f); - float4 co3 = (float4)(0.0625f, 0.0625f, 0.0625f, 0.0625f); - - - if(eveny) - { - sum = sum + ( evenFlag * co3) * s_srcPatch[1 + (tidy >> 1)][1 + ((tidx - 2) >> 1)]; - sum = sum + ( oddFlag * co2 ) * s_srcPatch[1 + (tidy >> 1)][1 + ((tidx - 1) >> 1)]; - sum = sum + ( evenFlag * co1) * s_srcPatch[1 + (tidy >> 1)][1 + ((tidx ) >> 1)]; - sum = sum + ( oddFlag * co2 ) * s_srcPatch[1 + (tidy >> 1)][1 + ((tidx + 1) >> 1)]; - sum = sum + ( evenFlag * co3) * s_srcPatch[1 + (tidy >> 1)][1 + ((tidx + 2) >> 1)]; - - } - - s_dstPatch[2 + tidy][tidx] = sum; - - if (tidy < 2) - { - sum = 0; - - if (eveny) - { - sum = sum + (evenFlag * co3) * s_srcPatch[lsizey-16][1 + ((tidx - 2) >> 1)]; - sum = sum + ( oddFlag * co2) * s_srcPatch[lsizey-16][1 + ((tidx - 1) >> 1)]; - sum = sum + (evenFlag * co1) * s_srcPatch[lsizey-16][1 + ((tidx ) >> 1)]; - sum = sum + ( oddFlag * co2) * s_srcPatch[lsizey-16][1 + ((tidx + 1) >> 1)]; - sum = sum + (evenFlag * co3) * s_srcPatch[lsizey-16][1 + ((tidx + 2) >> 1)]; - } - - s_dstPatch[tidy][tidx] = sum; - } - - if (tidy > 13) - { - sum = 0; - - if (eveny) - { - sum = sum + (evenFlag * co3) * s_srcPatch[lsizey-7][1 + ((tidx - 2) >> 1)]; - sum = sum + ( oddFlag * co2) * s_srcPatch[lsizey-7][1 + ((tidx - 1) >> 1)]; - sum = sum + (evenFlag * co1) * s_srcPatch[lsizey-7][1 + ((tidx ) >> 1)]; - sum = sum + ( oddFlag * co2) * s_srcPatch[lsizey-7][1 + ((tidx + 1) >> 1)]; - sum = sum + (evenFlag * co3) * s_srcPatch[lsizey-7][1 + ((tidx + 2) >> 1)]; - - } - s_dstPatch[4 + tidy][tidx] = sum; - } - - barrier(CLK_LOCAL_MEM_FENCE); - - sum = co3 * s_dstPatch[2 + tidy - 2][tidx]; - sum = sum + co2 * s_dstPatch[2 + tidy - 1][tidx]; - sum = sum + co1 * s_dstPatch[2 + tidy ][tidx]; - sum = sum + co2 * s_dstPatch[2 + tidy + 1][tidx]; - sum = sum + co3 * s_dstPatch[2 + tidy + 2][tidx]; - - if ((x < dstCols) && (y < dstRows)) - dst[x + y * dstStep] = convert_uchar4_sat_rte(4.0f * sum); -} - -/////////////////////////////////////////////////////////////////////// -////////////////////////// CV_16UC4 ////////////////////////////////// -/////////////////////////////////////////////////////////////////////// - -__kernel void pyrUp_C4_D2(__global ushort4* src, __global ushort4* dst, - int srcRows, int dstRows, int srcCols, int dstCols, - int srcOffset, int dstOffset, int srcStep, int dstStep) -{ - const int x = get_global_id(0); - const int y = get_global_id(1); - - __local float4 s_srcPatch[10][10]; - __local float4 s_dstPatch[20][16]; - - srcOffset >>= 3; - dstOffset >>= 3; - srcStep >>= 3; - dstStep >>= 3; - - - if( get_local_id(0) < 10 && get_local_id(1) < 10 ) - { - int srcx = (int)(get_group_id(0) * get_local_size(0) / 2 + get_local_id(0)) - 1; - int srcy = (int)(get_group_id(1) * get_local_size(1) / 2 + get_local_id(1)) - 1; - - srcx = abs(srcx); - srcx = min(srcCols - 1,srcx); - - srcy = abs(srcy); - srcy = min(srcRows -1 ,srcy); - - s_srcPatch[get_local_id(1)][get_local_id(0)] = convert_float4(src[srcx + srcy * srcStep]); - } - - barrier(CLK_LOCAL_MEM_FENCE); - - float4 sum = (float4)(0,0,0,0); - - const float4 evenFlag = (float4)((get_local_id(0) & 1) == 0); - const float4 oddFlag = (float4)((get_local_id(0) & 1) != 0); - const bool eveny = ((get_local_id(1) & 1) == 0); - const int tidx = get_local_id(0); - - float4 co1 = (float4)(0.375f, 0.375f, 0.375f, 0.375f); - float4 co2 = (float4)(0.25f, 0.25f, 0.25f, 0.25f); - float4 co3 = (float4)(0.0625f, 0.0625f, 0.0625f, 0.0625f); - - - if(eveny) - { - sum = sum + ( evenFlag* co3 ) * s_srcPatch[1 + (get_local_id(1) >> 1)][1 + ((tidx - 2) >> 1)]; - sum = sum + ( oddFlag * co2 ) * s_srcPatch[1 + (get_local_id(1) >> 1)][1 + ((tidx - 1) >> 1)]; - sum = sum + ( evenFlag* co1 ) * s_srcPatch[1 + (get_local_id(1) >> 1)][1 + ((tidx ) >> 1)]; - sum = sum + ( oddFlag * co2 ) * s_srcPatch[1 + (get_local_id(1) >> 1)][1 + ((tidx + 1) >> 1)]; - sum = sum + ( evenFlag* co3 ) * s_srcPatch[1 + (get_local_id(1) >> 1)][1 + ((tidx + 2) >> 1)]; - - } - - s_dstPatch[2 + get_local_id(1)][get_local_id(0)] = sum; - - if (get_local_id(1) < 2) - { - sum = 0; - - if (eveny) - { - sum = sum + (evenFlag * co3 ) * s_srcPatch[0][1 + ((tidx - 2) >> 1)]; - sum = sum + (oddFlag * co2 ) * s_srcPatch[0][1 + ((tidx - 1) >> 1)]; - sum = sum + (evenFlag * co1 ) * s_srcPatch[0][1 + ((tidx ) >> 1)]; - sum = sum + (oddFlag * co2 ) * s_srcPatch[0][1 + ((tidx + 1) >> 1)]; - sum = sum + (evenFlag * co3 ) * s_srcPatch[0][1 + ((tidx + 2) >> 1)]; - } - - s_dstPatch[get_local_id(1)][get_local_id(0)] = sum; - } - - if (get_local_id(1) > 13) - { - sum = 0; - - if (eveny) - { - sum = sum + (evenFlag * co3) * s_srcPatch[9][1 + ((tidx - 2) >> 1)]; - sum = sum + ( oddFlag * co2) * s_srcPatch[9][1 + ((tidx - 1) >> 1)]; - sum = sum + (evenFlag * co1) * s_srcPatch[9][1 + ((tidx ) >> 1)]; - sum = sum + ( oddFlag * co2) * s_srcPatch[9][1 + ((tidx + 1) >> 1)]; - sum = sum + (evenFlag * co3) * s_srcPatch[9][1 + ((tidx + 2) >> 1)]; - - } - s_dstPatch[4 + get_local_id(1)][get_local_id(0)] = sum; - } - - barrier(CLK_LOCAL_MEM_FENCE); - - sum = 0; - - const int tidy = get_local_id(1); - - sum = sum + co3 * s_dstPatch[2 + tidy - 2][get_local_id(0)]; - sum = sum + co2 * s_dstPatch[2 + tidy - 1][get_local_id(0)]; - sum = sum + co1 * s_dstPatch[2 + tidy ][get_local_id(0)]; - sum = sum + co2 * s_dstPatch[2 + tidy + 1][get_local_id(0)]; - sum = sum + co3 * s_dstPatch[2 + tidy + 2][get_local_id(0)]; - - if ((x < dstCols) && (y < dstRows)) - dst[x + y * dstStep] = convert_ushort4_sat_rte(4.0f * sum); -} - -/////////////////////////////////////////////////////////////////////// -////////////////////////// CV_16SC4 ////////////////////////////////// -/////////////////////////////////////////////////////////////////////// - -__kernel void pyrUp_C4_D3(__global short4* src, __global short4* dst, - int srcRows, int dstRows, int srcCols, int dstCols, - int srcOffset, int dstOffset, int srcStep, int dstStep) -{ - const int x = get_global_id(0); - const int y = get_global_id(1); - - __local float4 s_srcPatch[10][10]; - __local float4 s_dstPatch[20][16]; - - srcOffset >>= 3; - dstOffset >>= 3; - srcStep >>= 3; - dstStep >>= 3; - - if( get_local_id(0) < 10 && get_local_id(1) < 10 ) - { - int srcx = (int)(get_group_id(0) * get_local_size(0) / 2 + get_local_id(0)) - 1; - int srcy = (int)(get_group_id(1) * get_local_size(1) / 2 + get_local_id(1)) - 1; - - srcx = abs(srcx); - srcx = min(srcCols - 1,srcx); - - srcy = abs(srcy); - srcy = min(srcRows -1 ,srcy); - - s_srcPatch[get_local_id(1)][get_local_id(0)] = convert_float4(src[srcx + srcy * srcStep]); - } - - barrier(CLK_LOCAL_MEM_FENCE); - - float4 sum = (float4)(0,0,0,0); - - const float4 evenFlag = (float4)((get_local_id(0) & 1) == 0); - const float4 oddFlag = (float4)((get_local_id(0) & 1) != 0); - const bool eveny = ((get_local_id(1) & 1) == 0); - const int tidx = get_local_id(0); - - float4 co1 = (float4)(0.375f, 0.375f, 0.375f, 0.375f); - float4 co2 = (float4)(0.25f, 0.25f, 0.25f, 0.25f); - float4 co3 = (float4)(0.0625f, 0.0625f, 0.0625f, 0.0625f); - - - if(eveny) - { - sum = sum + ( evenFlag* co3 ) * s_srcPatch[1 + (get_local_id(1) >> 1)][1 + ((tidx - 2) >> 1)]; - sum = sum + ( oddFlag * co2 ) * s_srcPatch[1 + (get_local_id(1) >> 1)][1 + ((tidx - 1) >> 1)]; - sum = sum + ( evenFlag* co1 ) * s_srcPatch[1 + (get_local_id(1) >> 1)][1 + ((tidx ) >> 1)]; - sum = sum + ( oddFlag * co2 ) * s_srcPatch[1 + (get_local_id(1) >> 1)][1 + ((tidx + 1) >> 1)]; - sum = sum + ( evenFlag* co3 ) * s_srcPatch[1 + (get_local_id(1) >> 1)][1 + ((tidx + 2) >> 1)]; - - } - - s_dstPatch[2 + get_local_id(1)][get_local_id(0)] = sum; - - if (get_local_id(1) < 2) - { - sum = 0; - - if (eveny) - { - sum = sum + (evenFlag * co3 ) * s_srcPatch[0][1 + ((tidx - 2) >> 1)]; - sum = sum + (oddFlag * co2 ) * s_srcPatch[0][1 + ((tidx - 1) >> 1)]; - sum = sum + (evenFlag * co1 ) * s_srcPatch[0][1 + ((tidx ) >> 1)]; - sum = sum + (oddFlag * co2 ) * s_srcPatch[0][1 + ((tidx + 1) >> 1)]; - sum = sum + (evenFlag * co3 ) * s_srcPatch[0][1 + ((tidx + 2) >> 1)]; - } - - s_dstPatch[get_local_id(1)][get_local_id(0)] = sum; - } - - if (get_local_id(1) > 13) - { - sum = 0; - - if (eveny) - { - sum = sum + (evenFlag * co3) * s_srcPatch[9][1 + ((tidx - 2) >> 1)]; - sum = sum + ( oddFlag * co2) * s_srcPatch[9][1 + ((tidx - 1) >> 1)]; - sum = sum + (evenFlag * co1) * s_srcPatch[9][1 + ((tidx ) >> 1)]; - sum = sum + ( oddFlag * co2) * s_srcPatch[9][1 + ((tidx + 1) >> 1)]; - sum = sum + (evenFlag * co3) * s_srcPatch[9][1 + ((tidx + 2) >> 1)]; - - } - s_dstPatch[4 + get_local_id(1)][get_local_id(0)] = sum; - } - - barrier(CLK_LOCAL_MEM_FENCE); - - sum = 0; - - const int tidy = get_local_id(1); - - sum = sum + co3 * s_dstPatch[2 + tidy - 2][get_local_id(0)]; - sum = sum + co2 * s_dstPatch[2 + tidy - 1][get_local_id(0)]; - sum = sum + co1 * s_dstPatch[2 + tidy ][get_local_id(0)]; - sum = sum + co2 * s_dstPatch[2 + tidy + 1][get_local_id(0)]; - sum = sum + co3 * s_dstPatch[2 + tidy + 2][get_local_id(0)]; - - if ((x < dstCols) && (y < dstRows)) - dst[x + y * dstStep] = convert_short4_sat_rte(4.0f * sum); -} - -/////////////////////////////////////////////////////////////////////// -////////////////////////// CV_32FC4 ////////////////////////////////// -/////////////////////////////////////////////////////////////////////// - -__kernel void pyrUp_C4_D5(__global float4* src, __global float4* dst, - int srcRows, int dstRows, int srcCols, int dstCols, - int srcOffset, int dstOffset, int srcStep, int dstStep) -{ - const int x = get_global_id(0); - const int y = get_global_id(1); - const int tidx = get_local_id(0); - const int tidy = get_local_id(1); - const int lsizex = get_local_size(0); - const int lsizey = get_local_size(1); - __local float4 s_srcPatch[10][10]; - __local float4 s_dstPatch[20][16]; - - srcOffset >>= 4; - dstOffset >>= 4; - srcStep >>= 4; - dstStep >>= 4; - - - if( tidx < 10 && tidy < 10 ) - { - int srcx = (int)(get_group_id(0) * get_local_size(0) / 2 + tidx) - 1; - int srcy = (int)(get_group_id(1) * get_local_size(1) / 2 + tidy) - 1; - - srcx = abs(srcx); - srcx = min(srcCols - 1,srcx); - - srcy = abs(srcy); - srcy = min(srcRows -1 ,srcy); - - s_srcPatch[tidy][tidx] = (float4)(src[srcx + srcy * srcStep]); + s_srcPatch[tidy][tidx] = convertToFloat(src[srcx + srcy * srcStep]); } barrier(CLK_LOCAL_MEM_FENCE); - float4 sum = (float4)(0,0,0,0); - - const float4 evenFlag = (float4)((tidx & 1) == 0); - const float4 oddFlag = (float4)((tidx & 1) != 0); + floatType sum = (floatType)0; + const floatType evenFlag = (floatType)((tidx & 1) == 0); + const floatType oddFlag = (floatType)((tidx & 1) != 0); const bool eveny = ((tidy & 1) == 0); - float4 co1 = (float4)(0.375f, 0.375f, 0.375f, 0.375f); - float4 co2 = (float4)(0.25f, 0.25f, 0.25f, 0.25f); - float4 co3 = (float4)(0.0625f, 0.0625f, 0.0625f, 0.0625f); - + const floatType co1 = (floatType)0.375f; + const floatType co2 = (floatType)0.25f; + const floatType co3 = (floatType)0.0625f; if(eveny) { - sum = sum + ( evenFlag* co3 ) * s_srcPatch[1 + (tidy >> 1)][1 + ((tidx - 2) >> 1)]; + sum = ( evenFlag* co3 ) * s_srcPatch[1 + (tidy >> 1)][1 + ((tidx - 2) >> 1)]; sum = sum + ( oddFlag * co2 ) * s_srcPatch[1 + (tidy >> 1)][1 + ((tidx - 1) >> 1)]; sum = sum + ( evenFlag* co1 ) * s_srcPatch[1 + (tidy >> 1)][1 + ((tidx ) >> 1)]; sum = sum + ( oddFlag * co2 ) * s_srcPatch[1 + (tidy >> 1)][1 + ((tidx + 1) >> 1)]; sum = sum + ( evenFlag* co3 ) * s_srcPatch[1 + (tidy >> 1)][1 + ((tidx + 2) >> 1)]; - } s_dstPatch[2 + tidy][tidx] = sum; @@ -837,8 +108,8 @@ __kernel void pyrUp_C4_D5(__global float4* src, __global float4* dst, if (eveny) { - sum = sum + (evenFlag * co3 ) * s_srcPatch[lsizey-16][1 + ((tidx - 2) >> 1)]; - sum = sum + (oddFlag * co2 ) * s_srcPatch[lsizey-16][1 + ((tidx - 1) >> 1)]; + sum = (evenFlag * co3 ) * s_srcPatch[lsizey-16][1 + ((tidx - 2) >> 1)]; + sum = sum + ( oddFlag * co2 ) * s_srcPatch[lsizey-16][1 + ((tidx - 1) >> 1)]; sum = sum + (evenFlag * co1 ) * s_srcPatch[lsizey-16][1 + ((tidx ) >> 1)]; sum = sum + ( oddFlag * co2 ) * s_srcPatch[lsizey-16][1 + ((tidx + 1) >> 1)]; sum = sum + (evenFlag * co3 ) * s_srcPatch[lsizey-16][1 + ((tidx + 2) >> 1)]; @@ -853,24 +124,23 @@ __kernel void pyrUp_C4_D5(__global float4* src, __global float4* dst, if (eveny) { - sum = sum + (evenFlag * co3) * s_srcPatch[lsizey-7][1 + ((tidx - 2) >> 1)]; + sum = (evenFlag * co3) * s_srcPatch[lsizey-7][1 + ((tidx - 2) >> 1)]; sum = sum + ( oddFlag * co2) * s_srcPatch[lsizey-7][1 + ((tidx - 1) >> 1)]; sum = sum + (evenFlag * co1) * s_srcPatch[lsizey-7][1 + ((tidx ) >> 1)]; sum = sum + ( oddFlag * co2) * s_srcPatch[lsizey-7][1 + ((tidx + 1) >> 1)]; sum = sum + (evenFlag * co3) * s_srcPatch[lsizey-7][1 + ((tidx + 2) >> 1)]; - } s_dstPatch[4 + tidy][tidx] = sum; } barrier(CLK_LOCAL_MEM_FENCE); - sum = co3 * s_dstPatch[2 + tidy - 2][tidx]; + sum = co3 * s_dstPatch[2 + tidy - 2][tidx]; sum = sum + co2 * s_dstPatch[2 + tidy - 1][tidx]; sum = sum + co1 * s_dstPatch[2 + tidy ][tidx]; sum = sum + co2 * s_dstPatch[2 + tidy + 1][tidx]; sum = sum + co3 * s_dstPatch[2 + tidy + 2][tidx]; if ((x < dstCols) && (y < dstRows)) - dst[x + y * dstStep] = 4.0f * sum; + dst[x + y * dstStep] = convertToType(4.0f * sum); } diff --git a/modules/ocl/src/pyrup.cpp b/modules/ocl/src/pyrup.cpp index 95a2915f41..043031072c 100644 --- a/modules/ocl/src/pyrup.cpp +++ b/modules/ocl/src/pyrup.cpp @@ -59,9 +59,10 @@ namespace cv namespace ocl { extern const char *pyr_up; + void pyrUp(const cv::ocl::oclMat &src, cv::ocl::oclMat &dst) { - int depth = src.depth(), channels = src.channels(); + int depth = src.depth(), channels = src.channels(), oclChannels = src.oclchannels(); CV_Assert(depth == CV_8U || depth == CV_16U || depth == CV_16S || depth == CV_32F); CV_Assert(channels == 1 || channels == 3 || channels == 4); @@ -70,7 +71,17 @@ namespace cv Context *clCxt = src.clCxt; + const char * const typeMap[] = { "uchar", "char", "ushort", "short", "int", "float" }; + char buildOptions[250], convertString[50]; + const char * const channelsString = oclChannels == 1 ? "" : "4"; + sprintf(convertString, "convert_%s%s_sat_rte", typeMap[depth], channelsString); + sprintf(buildOptions, "-D Type=%s%s -D floatType=float%s -D convertToType=%s -D convertToFloat=%s", + typeMap[depth], channelsString, channelsString, + depth == CV_32F ? "" : convertString, + oclChannels == 4 ? "convert_float4" : "(float)"); + const std::string kernelName = "pyrUp"; + int dststep = dst.step / dst.elemSize(), srcstep = src.step / src.elemSize(); std::vector< pair > args; args.push_back( make_pair( sizeof(cl_mem), (void *)&src.data)); @@ -81,14 +92,15 @@ namespace cv args.push_back( make_pair( sizeof(cl_int), (void *)&dst.cols)); args.push_back( make_pair( sizeof(cl_int), (void *)&src.offset)); args.push_back( make_pair( sizeof(cl_int), (void *)&dst.offset)); - args.push_back( make_pair( sizeof(cl_int), (void *)&src.step)); - args.push_back( make_pair( sizeof(cl_int), (void *)&dst.step)); + args.push_back( make_pair( sizeof(cl_int), (void *)&srcstep)); + args.push_back( make_pair( sizeof(cl_int), (void *)&dststep)); size_t globalThreads[3] = {dst.cols, dst.rows, 1}; size_t localThreads[3] = {16, 16, 1}; - openCLExecuteKernel(clCxt, &pyr_up, kernelName, globalThreads, localThreads, args, src.oclchannels(), src.depth()); + openCLExecuteKernel(clCxt, &pyr_up, kernelName, globalThreads, localThreads, args, -1, -1, + buildOptions); } } } From e0325fe0576d9c385b70f9dc20facdcd5937f0c6 Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Fri, 13 Sep 2013 11:27:25 +0400 Subject: [PATCH 26/68] removed useless output --- modules/ocl/test/test_imgproc.cpp | 5 +---- modules/ocl/test/test_match_template.cpp | 9 --------- modules/ocl/test/test_objdetect.cpp | 7 ++----- 3 files changed, 3 insertions(+), 18 deletions(-) diff --git a/modules/ocl/test/test_imgproc.cpp b/modules/ocl/test/test_imgproc.cpp index c82683ca06..4d297a7a4f 100644 --- a/modules/ocl/test/test_imgproc.cpp +++ b/modules/ocl/test/test_imgproc.cpp @@ -482,7 +482,6 @@ struct CopyMakeBorder : ImgprocTestBase {}; TEST_P(CopyMakeBorder, Mat) { int bordertype[] = {cv::BORDER_CONSTANT, cv::BORDER_REPLICATE, cv::BORDER_REFLECT, cv::BORDER_WRAP, cv::BORDER_REFLECT_101}; - //const char *borderstr[] = {"BORDER_CONSTANT", "BORDER_REPLICATE", "BORDER_REFLECT", "BORDER_WRAP", "BORDER_REFLECT_101"}; cv::RNG &rng = TS::ptr()->get_rng(); int top = rng.uniform(0, 10); int bottom = rng.uniform(0, 10); @@ -895,8 +894,7 @@ TEST_P(Remap, Mat) return; } int bordertype[] = {cv::BORDER_CONSTANT, cv::BORDER_REPLICATE/*,BORDER_REFLECT,BORDER_WRAP,BORDER_REFLECT_101*/}; - //const char *borderstr[] = {"BORDER_CONSTANT", "BORDER_REPLICATE"/*, "BORDER_REFLECT","BORDER_WRAP","BORDER_REFLECT_101"*/}; - // for(int i = 0; i < sizeof(bordertype)/sizeof(int); i++) + for(int j = 0; j < LOOP_TIMES; j++) { random_roi(); @@ -908,7 +906,6 @@ TEST_P(Remap, Mat) if(interpolation == 0) EXPECT_MAT_NEAR(dst, cpu_dst, 1.0); EXPECT_MAT_NEAR(dst, cpu_dst, 2.0); - } } diff --git a/modules/ocl/test/test_match_template.cpp b/modules/ocl/test/test_match_template.cpp index 551c9ff12a..651d34b81b 100644 --- a/modules/ocl/test/test_match_template.cpp +++ b/modules/ocl/test/test_match_template.cpp @@ -52,8 +52,6 @@ IMPLEMENT_PARAM_CLASS(TemplateSize, cv::Size); -const char *TEMPLATE_METHOD_NAMES[6] = {"TM_SQDIFF", "TM_SQDIFF_NORMED", "TM_CCORR", "TM_CCORR_NORMED", "TM_CCOEFF", "TM_CCOEFF_NORMED"}; - #define MTEMP_SIZES testing::Values(cv::Size(128, 256), cv::Size(1024, 768)) PARAM_TEST_CASE(MatchTemplate8U, cv::Size, TemplateSize, Channels, TemplateMethod) @@ -74,12 +72,6 @@ PARAM_TEST_CASE(MatchTemplate8U, cv::Size, TemplateSize, Channels, TemplateMetho TEST_P(MatchTemplate8U, Accuracy) { - - std::cout << "Method: " << TEMPLATE_METHOD_NAMES[method] << std::endl; - std::cout << "Image Size: (" << size.width << ", " << size.height << ")" << std::endl; - std::cout << "Template Size: (" << templ_size.width << ", " << templ_size.height << ")" << std::endl; - std::cout << "Channels: " << cn << std::endl; - cv::Mat image = randomMat(size, CV_MAKETYPE(CV_8U, cn)); cv::Mat templ = randomMat(templ_size, CV_MAKETYPE(CV_8U, cn)); @@ -101,7 +93,6 @@ PARAM_TEST_CASE(MatchTemplate32F, cv::Size, TemplateSize, Channels, TemplateMeth cv::Size templ_size; int cn; int method; - //std::vector oclinfo; virtual void SetUp() { diff --git a/modules/ocl/test/test_objdetect.cpp b/modules/ocl/test/test_objdetect.cpp index ab1f849022..5da36b386a 100644 --- a/modules/ocl/test/test_objdetect.cpp +++ b/modules/ocl/test/test_objdetect.cpp @@ -239,11 +239,8 @@ TEST_P(Haar, FaceDetect) TEST_P(Haar, FaceDetectUseBuf) { ocl::OclCascadeClassifierBuf cascadebuf; - if(!cascadebuf.load(cascadeName)) - { - std::cout << "ERROR: Could not load classifier cascade for FaceDetectUseBuf!" << std::endl; - return; - } + ASSERT_TRUE(cascadebuf.load(cascadeName)) << "could not load classifier cascade for FaceDetectUseBuf!"; + cascadebuf.detectMultiScale(d_img, oclfaces, 1.1, 3, flags, Size(30, 30), Size(0, 0)); From 58b84c2fc09dd530826624e1ac3afc57d0132806 Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Mon, 16 Sep 2013 15:11:56 +0400 Subject: [PATCH 27/68] removed needless divUp --- modules/nonfree/src/surf.ocl.cpp | 6 - modules/ocl/include/opencv2/ocl/ocl.hpp | 5 + modules/ocl/src/arithm.cpp | 124 ++++----------------- modules/ocl/src/canny.cpp | 4 +- modules/ocl/src/filtering.cpp | 21 +--- modules/ocl/src/hog.cpp | 8 +- modules/ocl/src/imgproc.cpp | 19 +--- modules/ocl/src/initialization.cpp | 16 +-- modules/ocl/src/matrix_operations.cpp | 11 +- modules/ocl/src/mcwutil.cpp | 6 - modules/ocl/src/optical_flow_farneback.cpp | 24 +--- modules/ocl/src/split_merge.cpp | 115 +------------------ modules/ocl/src/stereo_csbp.cpp | 50 ++------- modules/ocl/src/stereobm.cpp | 17 +-- modules/ocl/src/stereobp.cpp | 5 +- 15 files changed, 64 insertions(+), 367 deletions(-) diff --git a/modules/nonfree/src/surf.ocl.cpp b/modules/nonfree/src/surf.ocl.cpp index de7cac2fdc..f8c1ad7294 100644 --- a/modules/nonfree/src/surf.ocl.cpp +++ b/modules/nonfree/src/surf.ocl.cpp @@ -82,12 +82,6 @@ namespace cv } } - -static inline size_t divUp(size_t total, size_t grain) -{ - return (total + grain - 1) / grain; -} - static inline int calcSize(int octave, int layer) { /* Wavelet size at first layer of first octave. */ diff --git a/modules/ocl/include/opencv2/ocl/ocl.hpp b/modules/ocl/include/opencv2/ocl/ocl.hpp index c117d0b2fb..42ac758408 100644 --- a/modules/ocl/include/opencv2/ocl/ocl.hpp +++ b/modules/ocl/include/opencv2/ocl/ocl.hpp @@ -1887,6 +1887,11 @@ namespace cv oclMat temp4; oclMat temp5; }; + + static inline size_t divUp(size_t total, size_t grain) + { + return (total + grain - 1) / grain; + } } } #if defined _MSC_VER && _MSC_VER >= 1200 diff --git a/modules/ocl/src/arithm.cpp b/modules/ocl/src/arithm.cpp index 819c013909..0cc803d199 100644 --- a/modules/ocl/src/arithm.cpp +++ b/modules/ocl/src/arithm.cpp @@ -108,13 +108,6 @@ namespace cv } } -////////////////////////////////////////////////////////////////////////// -//////////////////common///////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////// -inline int divUp(int total, int grain) -{ - return (total + grain - 1) / grain; -} ////////////////////////////////////////////////////////////////////////////// /////////////////////// add subtract multiply divide ///////////////////////// ////////////////////////////////////////////////////////////////////////////// @@ -150,10 +143,7 @@ void arithmetic_run(const oclMat &src1, const oclMat &src2, oclMat &dst, int cols = divUp(dst.cols * channels + offset_cols, vector_length); size_t localThreads[3] = { 64, 4, 1 }; - size_t globalThreads[3] = { divUp(cols, localThreads[0]) *localThreads[0], - divUp(dst.rows, localThreads[1]) *localThreads[1], - 1 - }; + size_t globalThreads[3] = { cols, dst.rows, 1 }; int dst_step1 = dst.cols * dst.elemSize(); vector > args; @@ -226,10 +216,7 @@ static void arithmetic_run(const oclMat &src1, const oclMat &src2, oclMat &dst, int cols = divUp(dst.cols + offset_cols, vector_length); size_t localThreads[3] = { 64, 4, 1 }; - size_t globalThreads[3] = { divUp(cols, localThreads[0]) *localThreads[0], - divUp(dst.rows, localThreads[1]) *localThreads[1], - 1 - }; + size_t globalThreads[3] = { cols, dst.rows, 1 }; int dst_step1 = dst.cols * dst.elemSize(); vector > args; @@ -338,10 +325,7 @@ void arithmetic_scalar_run(const oclMat &src1, const Scalar &src2, oclMat &dst, int cols = divUp(dst.cols + offset_cols, vector_length); size_t localThreads[3] = { 64, 4, 1 }; - size_t globalThreads[3] = { divUp(cols, localThreads[0]) *localThreads[0], - divUp(dst.rows, localThreads[1]) *localThreads[1], - 1 - }; + size_t globalThreads[3] = { cols, dst.rows, 1 }; int dst_step1 = dst.cols * dst.elemSize(); vector > args; @@ -397,10 +381,7 @@ static void arithmetic_scalar_run(const oclMat &src, oclMat &dst, string kernelN int cols = divUp(dst.cols * channels + offset_cols, vector_length); size_t localThreads[3] = { 64, 4, 1 }; - size_t globalThreads[3] = { divUp(cols, localThreads[0]) *localThreads[0], - divUp(dst.rows, localThreads[1]) *localThreads[1], - 1 - }; + size_t globalThreads[3] = { cols, dst.rows, 1 }; int dst_step1 = dst.cols * dst.elemSize(); vector > args; @@ -515,10 +496,8 @@ static void compare_run(const oclMat &src1, const oclMat &src2, oclMat &dst, str int offset_cols = (dst.offset / dst.elemSize1()) & (vector_length - 1); int cols = divUp(dst.cols + offset_cols, vector_length); size_t localThreads[3] = { 64, 4, 1 }; - size_t globalThreads[3] = { divUp(cols, localThreads[0]) *localThreads[0], - divUp(dst.rows, localThreads[1]) *localThreads[1], - 1 - }; + size_t globalThreads[3] = { cols, dst.rows, 1 }; + int dst_step1 = dst.cols * dst.elemSize(); vector > args; args.push_back( make_pair( sizeof(cl_mem), (void *)&src1.data )); @@ -945,10 +924,7 @@ static void arithmetic_flip_rows_run(const oclMat &src, oclMat &dst, string kern int rows = divUp(dst.rows, 2); size_t localThreads[3] = { 64, 4, 1 }; - size_t globalThreads[3] = { divUp(cols, localThreads[0]) *localThreads[0], - divUp(rows, localThreads[1]) *localThreads[1], - 1 - }; + size_t globalThreads[3] = { cols, rows, 1 }; int dst_step1 = dst.cols * dst.elemSize(); vector > args; @@ -993,10 +969,7 @@ static void arithmetic_flip_cols_run(const oclMat &src, oclMat &dst, string kern int rows = isVertical ? divUp(dst.rows, 2) : dst.rows; size_t localThreads[3] = { 64, 4, 1 }; - size_t globalThreads[3] = { divUp(cols, localThreads[0]) *localThreads[0], - divUp(rows, localThreads[1]) *localThreads[1], - 1 - }; + size_t globalThreads[3] = { cols, rows, 1 }; int dst_step1 = dst.cols * dst.elemSize(); vector > args; @@ -1156,10 +1129,7 @@ static void arithmetic_exp_log_run(const oclMat &src, oclMat &dst, string kernel int depth = dst.depth(); size_t localThreads[3] = { 64, 4, 1 }; - size_t globalThreads[3] = { divUp(dst.cols, localThreads[0]) *localThreads[0], - divUp(dst.rows, localThreads[1]) *localThreads[1], - 1 - }; + size_t globalThreads[3] = { dst.cols, dst.rows, 1 }; vector > args; args.push_back( make_pair( sizeof(cl_int), (void *)&src.rows )); @@ -1201,13 +1171,9 @@ static void arithmetic_magnitude_phase_run(const oclMat &src1, const oclMat &src size_t vector_length = 1; int offset_cols = ((dst.offset % dst.step) / dst.elemSize1()) & (vector_length - 1); int cols = divUp(dst.cols * channels + offset_cols, vector_length); - int rows = dst.rows; size_t localThreads[3] = { 64, 4, 1 }; - size_t globalThreads[3] = { divUp(cols, localThreads[0]) *localThreads[0], - divUp(rows, localThreads[1]) *localThreads[1], - 1 - }; + size_t globalThreads[3] = { cols, dst.rows, 1 }; vector > args; args.push_back( make_pair( sizeof(cl_mem), (void *)&src1.data )); @@ -1252,13 +1218,9 @@ static void arithmetic_phase_run(const oclMat &src1, const oclMat &src2, oclMat size_t vector_length = 1; int offset_cols = ((dst.offset % dst.step) / dst.elemSize1()) & (vector_length - 1); int cols = divUp(dst.cols * channels + offset_cols, vector_length); - int rows = dst.rows; size_t localThreads[3] = { 64, 4, 1 }; - size_t globalThreads[3] = { divUp(cols, localThreads[0]) *localThreads[0], - divUp(rows, localThreads[1]) *localThreads[1], - 1 - }; + size_t globalThreads[3] = { cols, dst.rows, 1 }; int dst_step1 = dst.cols * dst.elemSize(); vector > args; @@ -1283,15 +1245,9 @@ void cv::ocl::phase(const oclMat &x, const oclMat &y, oclMat &Angle , bool angle Angle.create(x.size(), x.type()); string kernelName = angleInDegrees ? "arithm_phase_indegrees" : "arithm_phase_inradians"; if(angleInDegrees) - { arithmetic_phase_run(x, y, Angle, kernelName, &arithm_phase); - //cout<<"1"< > args; @@ -1333,7 +1285,7 @@ static void arithmetic_cartToPolar_run(const oclMat &src1, const oclMat &src2, o args.push_back( make_pair( sizeof(cl_mem), (void *)&dst_cart.data )); args.push_back( make_pair( sizeof(cl_int), (void *)&dst_cart.step )); args.push_back( make_pair( sizeof(cl_int), (void *)&dst_cart.offset )); - args.push_back( make_pair( sizeof(cl_int), (void *)&rows )); + args.push_back( make_pair( sizeof(cl_int), (void *)&src1.rows )); args.push_back( make_pair( sizeof(cl_int), (void *)&cols )); args.push_back( make_pair( sizeof(cl_int), (void *)&tmp )); @@ -1369,10 +1321,7 @@ static void arithmetic_ptc_run(const oclMat &src1, const oclMat &src2, oclMat &d int rows = src2.rows; size_t localThreads[3] = { 64, 4, 1 }; - size_t globalThreads[3] = { divUp(cols, localThreads[0]) *localThreads[0], - divUp(rows, localThreads[1]) *localThreads[1], - 1 - }; + size_t globalThreads[3] = { cols, rows, 1 }; int tmp = angleInDegrees ? 1 : 0; vector > args; @@ -1632,10 +1581,7 @@ static void bitwise_run(const oclMat &src1, oclMat &dst, string kernelName, cons int cols = divUp(dst.cols * channels + offset_cols, vector_length); size_t localThreads[3] = { 64, 4, 1 }; - size_t globalThreads[3] = { divUp(cols, localThreads[0]) *localThreads[0], - divUp(dst.rows, localThreads[1]) *localThreads[1], - 1 - }; + size_t globalThreads[3] = { cols, dst.rows, 1 }; int dst_step1 = dst.cols * dst.elemSize(); vector > args; @@ -1678,10 +1624,7 @@ void bitwise_run(const oclMat &src1, const oclMat &src2, oclMat &dst, string ker int cols = divUp(dst.cols * channels + offset_cols, vector_length); size_t localThreads[3] = { 64, 4, 1 }; - size_t globalThreads[3] = { divUp(cols, localThreads[0]) *localThreads[0], - divUp(dst.rows, localThreads[1]) *localThreads[1], - 1 - }; + size_t globalThreads[3] = { cols, dst.rows, 1 }; int dst_step1 = dst.cols * dst.elemSize(); vector > args; @@ -1739,10 +1682,7 @@ static void bitwise_run(const oclMat &src1, const oclMat &src2, oclMat &dst, int cols = divUp(dst.cols + offset_cols, vector_length); size_t localThreads[3] = { 64, 4, 1 }; - size_t globalThreads[3] = { divUp(cols, localThreads[0]) *localThreads[0], - divUp(dst.rows, localThreads[1]) *localThreads[1], - 1 - }; + size_t globalThreads[3] = { cols, dst.rows, 1 }; int dst_step1 = dst.cols * dst.elemSize(); vector > args; @@ -1800,10 +1740,7 @@ void bitwise_scalar_run(const oclMat &src1, const Scalar &src2, oclMat &dst, int cols = divUp(dst.cols + offset_cols, vector_length); size_t localThreads[3] = { 64, 4, 1 }; - size_t globalThreads[3] = { divUp(cols, localThreads[0]) *localThreads[0], - divUp(dst.rows, localThreads[1]) *localThreads[1], - 1 - }; + size_t globalThreads[3] = { cols, dst.rows, 1 }; int dst_step1 = dst.cols * dst.elemSize(); vector > args; @@ -2096,10 +2033,7 @@ static void transpose_run(const oclMat &src, oclMat &dst, string kernelName) int cols = divUp(src.cols + offset_cols, vector_length); size_t localThreads[3] = { TILE_DIM, BLOCK_ROWS, 1 }; - size_t globalThreads[3] = { divUp(cols, TILE_DIM) *localThreads[0], - divUp(src.rows, TILE_DIM) *localThreads[1], - 1 - }; + size_t globalThreads[3] = { cols, src.rows, 1 }; vector > args; args.push_back( make_pair( sizeof(cl_mem), (void *)&src.data )); @@ -2154,10 +2088,7 @@ void cv::ocl::addWeighted(const oclMat &src1, double alpha, const oclMat &src2, int cols = divUp(dst.cols * channels + offset_cols, vector_length); size_t localThreads[3] = { 256, 1, 1 }; - size_t globalThreads[3] = { divUp(cols, localThreads[0]) *localThreads[0], - divUp(dst.rows, localThreads[1]) *localThreads[1], - 1 - }; + size_t globalThreads[3] = { cols, dst.rows, 1}; int dst_step1 = dst.cols * dst.elemSize(); int src1_step = (int) src1.step; @@ -2220,10 +2151,7 @@ void cv::ocl::magnitudeSqr(const oclMat &src1, const oclMat &src2, oclMat &dst) int cols = divUp(dst.cols * channels + offset_cols, vector_length); size_t localThreads[3] = { 256, 1, 1 }; - size_t globalThreads[3] = { divUp(cols, localThreads[0]) *localThreads[0], - divUp(dst.rows, localThreads[1]) *localThreads[1], - 1 - }; + size_t globalThreads[3] = { cols, dst.rows, 1 }; int dst_step1 = dst.cols * dst.elemSize(); vector > args; @@ -2268,10 +2196,7 @@ void cv::ocl::magnitudeSqr(const oclMat &src1, oclMat &dst) int cols = divUp(dst.cols * channels + offset_cols, vector_length); size_t localThreads[3] = { 256, 1, 1 }; - size_t globalThreads[3] = { divUp(cols, localThreads[0]) *localThreads[0], - divUp(dst.rows, localThreads[1]) *localThreads[1], - 1 - }; + size_t globalThreads[3] = { cols, dst.rows, 1 }; int dst_step1 = dst.cols * dst.elemSize(); vector > args; @@ -2303,10 +2228,7 @@ static void arithmetic_pow_run(const oclMat &src1, double p, oclMat &dst, string int rows = dst.rows; size_t localThreads[3] = { 64, 4, 1 }; - size_t globalThreads[3] = { divUp(cols, localThreads[0]) *localThreads[0], - divUp(rows, localThreads[1]) *localThreads[1], - 1 - }; + size_t globalThreads[3] = { cols, rows, 1 }; int dst_step1 = dst.cols * dst.elemSize(); vector > args; diff --git a/modules/ocl/src/canny.cpp b/modules/ocl/src/canny.cpp index 82bb01bfdc..4c7b988f6f 100644 --- a/modules/ocl/src/canny.cpp +++ b/modules/ocl/src/canny.cpp @@ -360,14 +360,13 @@ void canny::edgesHysteresisGlobal_gpu(oclMat &map, oclMat &st1, oclMat &st2, voi vector< pair > args; size_t localThreads[3] = {128, 1, 1}; -#define DIVUP(a, b) ((a)+(b)-1)/(b) int count_i[1] = {0}; while(count > 0) { openCLSafeCall(clEnqueueWriteBuffer(*(cl_command_queue*)getoclCommandQueue(), (cl_mem)counter, 1, 0, sizeof(int), &count_i, 0, NULL, NULL)); args.clear(); - size_t globalThreads[3] = {std::min(count, 65535u) * 128, DIVUP(count, 65535), 1}; + size_t globalThreads[3] = {std::min(count, 65535u) * 128, divUp(count, 65535), 1}; args.push_back( make_pair( sizeof(cl_mem), (void *)&map.data)); args.push_back( make_pair( sizeof(cl_mem), (void *)&st1.data)); args.push_back( make_pair( sizeof(cl_mem), (void *)&st2.data)); @@ -382,7 +381,6 @@ void canny::edgesHysteresisGlobal_gpu(oclMat &map, oclMat &st1, oclMat &st2, voi openCLSafeCall(clEnqueueReadBuffer(*(cl_command_queue*)getoclCommandQueue(), (cl_mem)counter, 1, 0, sizeof(int), &count, 0, NULL, NULL)); std::swap(st1, st2); } -#undef DIVUP } void canny::getEdges_gpu(oclMat &map, oclMat &dst, int rows, int cols) diff --git a/modules/ocl/src/filtering.cpp b/modules/ocl/src/filtering.cpp index c0557980b2..e252d852cb 100644 --- a/modules/ocl/src/filtering.cpp +++ b/modules/ocl/src/filtering.cpp @@ -68,22 +68,12 @@ extern const char *filtering_adaptive_bilateral; } } -namespace -{ -inline int divUp(int total, int grain) -{ - return (total + grain - 1) / grain; -} -} - namespace { inline void normalizeAnchor(int &anchor, int ksize) { if (anchor < 0) - { anchor = ksize >> 1; - } CV_Assert(0 <= anchor && anchor < ksize); } @@ -97,9 +87,7 @@ inline void normalizeAnchor(Point &anchor, const Size &ksize) inline void normalizeROI(Rect &roi, const Size &ksize, const Point &anchor, const Size &src_size) { if (roi == Rect(0, 0, -1, -1)) - { roi = Rect(0, 0, src_size.width, src_size.height); - } CV_Assert(ksize.height > 0 && ksize.width > 0 && ((ksize.height & 1) == 1) && ((ksize.width & 1) == 1)); CV_Assert((anchor.x == -1 && anchor.y == -1) || (anchor.x == ksize.width >> 1 && anchor.y == ksize.height >> 1)); @@ -112,10 +100,7 @@ inline void normalizeKernel(const Mat &kernel, oclMat &gpu_krnl, int type = CV_8 int scale = nDivisor && (kernel.depth() == CV_32F || kernel.depth() == CV_64F) ? 256 : 1; if (nDivisor) - { *nDivisor = scale; - } - Mat temp(kernel.size(), type); kernel.convertTo(temp, type, scale); Mat cont_krnl = temp.reshape(1, 1); @@ -125,9 +110,7 @@ inline void normalizeKernel(const Mat &kernel, oclMat &gpu_krnl, int type = CV_8 int count = cont_krnl.cols >> 1; for (int i = 0; i < count; ++i) - { std::swap(cont_krnl.at(0, i), cont_krnl.at(0, cont_krnl.cols - 1 - i)); - } } gpu_krnl.upload(cont_krnl); @@ -627,8 +610,6 @@ static void GPUFilter2D(const oclMat &src, oclMat &dst, const oclMat &mat_kernel int localWidth = localThreads[0] + paddingPixels; int localHeight = localThreads[1] + paddingPixels; - // 260 = divup((localThreads[0] + filterWidth * 2), 4) * 4 - // 6 = (ROWS_PER_GROUP_WHICH_IS_4 + filterWidth * 2) size_t localMemSize = ksize_3x3 ? 260 * 6 * src.elemSize() : (localWidth * localHeight) * src.elemSize(); int vector_lengths[4][7] = {{4, 4, 4, 4, 4, 4, 4}, @@ -1713,4 +1694,4 @@ void cv::ocl::adaptiveBilateralFilter(const oclMat& src, oclMat& dst, Size ksize openCLExecuteKernel(Context::getContext(), &filtering_adaptive_bilateral, kernelName, globalThreads, localThreads, args, cn, depth, build_options); -} \ No newline at end of file +} diff --git a/modules/ocl/src/hog.cpp b/modules/ocl/src/hog.cpp index 4aafb47d9c..55872829a9 100644 --- a/modules/ocl/src/hog.cpp +++ b/modules/ocl/src/hog.cpp @@ -124,11 +124,6 @@ namespace cv using namespace ::cv::ocl::device; -static inline int divUp(int total, int grain) -{ - return (total + grain - 1) / grain; -} - cv::ocl::HOGDescriptor::HOGDescriptor(Size win_size_, Size block_size_, Size block_stride_, Size cell_size_, int nbins_, double win_sigma_, double threshold_L2hys_, bool gamma_correction_, int nlevels_) @@ -1671,7 +1666,8 @@ void cv::ocl::device::hog::compute_hists(int nbins, { openCLExecuteKernel(clCxt, &objdetect_hog, kernelName, globalThreads, localThreads, args, -1, -1, "-D CPU"); - }else + } + else { cl_kernel kernel = openCLGetKernelFromSource(clCxt, &objdetect_hog, kernelName); int wave_size = queryDeviceInfo(kernel); diff --git a/modules/ocl/src/imgproc.cpp b/modules/ocl/src/imgproc.cpp index ff509fb110..2ed786fe49 100644 --- a/modules/ocl/src/imgproc.cpp +++ b/modules/ocl/src/imgproc.cpp @@ -1518,11 +1518,6 @@ namespace cv // CLAHE namespace clahe { - inline int divUp(int total, int grain) - { - return (total + grain - 1) / grain * grain; - } - static void calcLut(const oclMat &src, oclMat &dst, const int tilesX, const int tilesY, const cv::Size tileSize, const int clipLimit, const float lutScale) @@ -1546,9 +1541,7 @@ namespace cv size_t globalThreads[3] = { tilesX * localThreads[0], tilesY * localThreads[1], 1 }; bool is_cpu = queryDeviceInfo(); if (is_cpu) - { openCLExecuteKernel(Context::getContext(), &imgproc_clahe, kernelName, globalThreads, localThreads, args, -1, -1, (char*)" -D CPU"); - } else { cl_kernel kernel = openCLGetKernelFromSource(Context::getContext(), &imgproc_clahe, kernelName); @@ -1583,7 +1576,7 @@ namespace cv String kernelName = "transform"; size_t localThreads[3] = { 32, 8, 1 }; - size_t globalThreads[3] = { divUp(src.cols, localThreads[0]), divUp(src.rows, localThreads[1]), 1 }; + size_t globalThreads[3] = { src.cols, src.rows, 1 }; openCLExecuteKernel(Context::getContext(), &imgproc_clahe, kernelName, globalThreads, localThreads, args, -1, -1); } @@ -1801,10 +1794,7 @@ namespace cv } } //////////////////////////////////convolve//////////////////////////////////////////////////// -inline int divUp(int total, int grain) -{ - return (total + grain - 1) / grain; -} + static void convolve_run(const oclMat &src, const oclMat &temp1, oclMat &dst, string kernelName, const char **kernelString) { CV_Assert(src.depth() == CV_32FC1); @@ -1826,10 +1816,7 @@ static void convolve_run(const oclMat &src, const oclMat &temp1, oclMat &dst, st int rows = dst.rows; size_t localThreads[3] = { 16, 16, 1 }; - size_t globalThreads[3] = { divUp(cols, localThreads[0]) *localThreads[0], - divUp(rows, localThreads[1]) *localThreads[1], - 1 - }; + size_t globalThreads[3] = { cols, rows, 1 }; vector > args; args.push_back( make_pair( sizeof(cl_mem), (void *)&src.data )); diff --git a/modules/ocl/src/initialization.cpp b/modules/ocl/src/initialization.cpp index 564b403572..8f5fae3f8d 100644 --- a/modules/ocl/src/initialization.cpp +++ b/modules/ocl/src/initialization.cpp @@ -285,11 +285,6 @@ namespace cv return 0; } - inline int divUp(int total, int grain) - { - return (total + grain - 1) / grain; - } - int getDevice(std::vector &oclinfo, int devicetype) { //TODO: cache oclinfo vector @@ -707,11 +702,10 @@ namespace cv if ( localThreads != NULL) { - globalThreads[0] = divUp(globalThreads[0], localThreads[0]) * localThreads[0]; - globalThreads[1] = divUp(globalThreads[1], localThreads[1]) * localThreads[1]; - globalThreads[2] = divUp(globalThreads[2], localThreads[2]) * localThreads[2]; + globalThreads[0] = alignSize(globalThreads[0], localThreads[0]); + globalThreads[1] = alignSize(globalThreads[1], localThreads[1]); + globalThreads[2] = alignSize(globalThreads[2], localThreads[2]); - //size_t blockSize = localThreads[0] * localThreads[1] * localThreads[2]; cv::ocl::openCLVerifyKernel(clCxt, kernel, localThreads); } for(size_t i = 0; i < args.size(); i ++) @@ -742,10 +736,6 @@ namespace cv execute_time = (double)(end_time - start_time) / (1000 * 1000); total_time = (double)(end_time - queue_time) / (1000 * 1000); - // cout << setiosflags(ios::left) << setw(15) << execute_time; - // cout << setiosflags(ios::left) << setw(15) << total_time - execute_time; - // cout << setiosflags(ios::left) << setw(15) << total_time << endl; - total_execute_time += execute_time; total_kernel_time += total_time; clReleaseEvent(event); diff --git a/modules/ocl/src/matrix_operations.cpp b/modules/ocl/src/matrix_operations.cpp index 82189b71e5..58369e93ee 100644 --- a/modules/ocl/src/matrix_operations.cpp +++ b/modules/ocl/src/matrix_operations.cpp @@ -307,11 +307,6 @@ void cv::ocl::oclMat::download(cv::Mat &m) const m.adjustROI(-ofs.y, ofs.y + rows - wholerows, -ofs.x, ofs.x + cols - wholecols); } -/////////////////////common////////////////////////////////////// -inline int divUp(int total, int grain) -{ - return (total + grain - 1) / grain; -} /////////////////////////////////////////////////////////////////////////// ////////////////////////////////// CopyTo ///////////////////////////////// /////////////////////////////////////////////////////////////////////////// @@ -331,11 +326,7 @@ static void copy_to_with_mask(const oclMat &src, oclMat &dst, const oclMat &mask char compile_option[32]; sprintf(compile_option, "-D GENTYPE=%s", string_types[dst.oclchannels() - 1][dst.depth()].c_str()); size_t localThreads[3] = {16, 16, 1}; - size_t globalThreads[3]; - - globalThreads[0] = divUp(dst.cols, localThreads[0]) * localThreads[0]; - globalThreads[1] = divUp(dst.rows, localThreads[1]) * localThreads[1]; - globalThreads[2] = 1; + size_t globalThreads[3] = { dst.cols, dst.rows, 1 }; int dststep_in_pixel = dst.step / dst.elemSize(), dstoffset_in_pixel = dst.offset / dst.elemSize(); int srcstep_in_pixel = src.step / src.elemSize(), srcoffset_in_pixel = src.offset / src.elemSize(); diff --git a/modules/ocl/src/mcwutil.cpp b/modules/ocl/src/mcwutil.cpp index 2966d53dba..fc94e2f3d8 100644 --- a/modules/ocl/src/mcwutil.cpp +++ b/modules/ocl/src/mcwutil.cpp @@ -71,12 +71,6 @@ namespace cv { namespace ocl { - - inline int divUp(int total, int grain) - { - return (total + grain - 1) / grain; - } - // provide additional methods for the user to interact with the command queue after a task is fired static void openCLExecuteKernel_2(Context *clCxt , const char **source, string kernelName, size_t globalThreads[3], size_t localThreads[3], vector< pair > &args, int channels, diff --git a/modules/ocl/src/optical_flow_farneback.cpp b/modules/ocl/src/optical_flow_farneback.cpp index e622446bb8..618a637f09 100644 --- a/modules/ocl/src/optical_flow_farneback.cpp +++ b/modules/ocl/src/optical_flow_farneback.cpp @@ -73,11 +73,6 @@ oclMat gKer; float ig[4]; -inline int divUp(int total, int grain) -{ - return (total + grain - 1) / grain; -} - inline void setGaussianBlurKernel(const float *c_gKer, int ksizeHalf) { cv::Mat t_gKer(1, ksizeHalf + 1, CV_32FC1, const_cast(c_gKer)); @@ -88,7 +83,7 @@ static void gaussianBlurOcl(const oclMat &src, int ksizeHalf, oclMat &dst) { string kernelName("gaussianBlur"); size_t localThreads[3] = { 256, 1, 1 }; - size_t globalThreads[3] = { divUp(src.cols, localThreads[0]) * localThreads[0], src.rows, 1 }; + size_t globalThreads[3] = { src.cols, src.rows, 1 }; int smem_size = (localThreads[0] + 2*ksizeHalf) * sizeof(float); CV_Assert(dst.size() == src.size()); @@ -138,10 +133,7 @@ static void updateMatricesOcl(const oclMat &flowx, const oclMat &flowy, const oc { string kernelName("updateMatrices"); size_t localThreads[3] = { 32, 8, 1 }; - size_t globalThreads[3] = { divUp(flowx.cols, localThreads[0]) * localThreads[0], - divUp(flowx.rows, localThreads[1]) * localThreads[1], - 1 - }; + size_t globalThreads[3] = { flowx.cols, flowx.rows, 1 }; std::vector< std::pair > args; args.push_back(std::make_pair(sizeof(cl_mem), (void *)&M.data)); @@ -166,7 +158,7 @@ static void boxFilter5Ocl(const oclMat &src, int ksizeHalf, oclMat &dst) string kernelName("boxFilter5"); int height = src.rows / 5; size_t localThreads[3] = { 256, 1, 1 }; - size_t globalThreads[3] = { divUp(src.cols, localThreads[0]) * localThreads[0], height, 1 }; + size_t globalThreads[3] = { src.cols, height, 1 }; int smem_size = (localThreads[0] + 2*ksizeHalf) * 5 * sizeof(float); std::vector< std::pair > args; @@ -188,10 +180,7 @@ static void updateFlowOcl(const oclMat &M, oclMat &flowx, oclMat &flowy) string kernelName("updateFlow"); int cols = divUp(flowx.cols, 4); size_t localThreads[3] = { 32, 8, 1 }; - size_t globalThreads[3] = { divUp(cols, localThreads[0]) * localThreads[0], - divUp(flowx.rows, localThreads[1]) * localThreads[0], - 1 - }; + size_t globalThreads[3] = { cols, flowx.rows, 1 }; std::vector< std::pair > args; args.push_back(std::make_pair(sizeof(cl_mem), (void *)&flowx.data)); @@ -211,9 +200,8 @@ static void gaussianBlur5Ocl(const oclMat &src, int ksizeHalf, oclMat &dst) { string kernelName("gaussianBlur5"); int height = src.rows / 5; - int width = src.cols; size_t localThreads[3] = { 256, 1, 1 }; - size_t globalThreads[3] = { divUp(width, localThreads[0]) * localThreads[0], height, 1 }; + size_t globalThreads[3] = { src.cols, height, 1 }; int smem_size = (localThreads[0] + 2*ksizeHalf) * 5 * sizeof(float); std::vector< std::pair > args; @@ -222,7 +210,7 @@ static void gaussianBlur5Ocl(const oclMat &src, int ksizeHalf, oclMat &dst) args.push_back(std::make_pair(sizeof(cl_mem), (void *)&gKer.data)); args.push_back(std::make_pair(smem_size, (void *)NULL)); args.push_back(std::make_pair(sizeof(cl_int), (void *)&height)); - args.push_back(std::make_pair(sizeof(cl_int), (void *)&width)); + args.push_back(std::make_pair(sizeof(cl_int), (void *)&src.cols)); args.push_back(std::make_pair(sizeof(cl_int), (void *)&dst.step)); args.push_back(std::make_pair(sizeof(cl_int), (void *)&src.step)); args.push_back(std::make_pair(sizeof(cl_int), (void *)&ksizeHalf)); diff --git a/modules/ocl/src/split_merge.cpp b/modules/ocl/src/split_merge.cpp index de3d2700a9..79bd0f0e21 100644 --- a/modules/ocl/src/split_merge.cpp +++ b/modules/ocl/src/split_merge.cpp @@ -73,61 +73,6 @@ namespace cv { namespace split_merge { - /////////////////////////////////////////////////////////// - ///////////////common///////////////////////////////////// - ///////////////////////////////////////////////////////// - inline int divUp(int total, int grain) - { - return (total + grain - 1) / grain; - } - //////////////////////////////////////////////////////////////////////////// - ////////////////////merge////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////// - // static void merge_vector_run_no_roi(const oclMat *mat_src, size_t n, oclMat &mat_dst) - // { - // Context *clCxt = mat_dst.clCxt; - // int channels = mat_dst.oclchannels(); - // int depth = mat_dst.depth(); - - // string kernelName = "merge_vector"; - - // int indexes[4][7] = {{0, 0, 0, 0, 0, 0, 0}, - // {4, 4, 2, 2, 1, 1, 1}, - // {4, 4, 2, 2 , 1, 1, 1}, - // {4, 4, 2, 2, 1, 1, 1} - // }; - - // size_t index = indexes[channels - 1][mat_dst.depth()]; - // int cols = divUp(mat_dst.cols, index); - // size_t localThreads[3] = { 64, 4, 1 }; - // size_t globalThreads[3] = { divUp(cols, localThreads[0]) *localThreads[0], - // divUp(mat_dst.rows, localThreads[1]) *localThreads[1], - // 1 - // }; - - // vector > args; - // args.push_back( make_pair( sizeof(cl_int), (void *)&mat_dst.rows)); - // args.push_back( make_pair( sizeof(cl_int), (void *)&cols)); - // args.push_back( make_pair( sizeof(cl_mem), (void *)&mat_dst.data)); - // args.push_back( make_pair( sizeof(cl_int), (void *)&mat_dst.step)); - // args.push_back( make_pair( sizeof(cl_mem), (void *)&mat_src[0].data)); - // args.push_back( make_pair( sizeof(cl_int), (void *)&mat_src[0].step)); - // args.push_back( make_pair( sizeof(cl_mem), (void *)&mat_src[1].data)); - // args.push_back( make_pair( sizeof(cl_int), (void *)&mat_src[1].step)); - // if(n >= 3) - // { - // args.push_back( make_pair( sizeof(cl_mem), (void *)&mat_src[2].data)); - // args.push_back( make_pair( sizeof(cl_int), (void *)&mat_src[2].step)); - // } - // if(n >= 4) - // { - // args.push_back( make_pair( sizeof(cl_mem), (void *)&mat_src[3].data)); - // args.push_back( make_pair( sizeof(cl_int), (void *)&mat_src[3].step)); - // } - - // openCLExecuteKernel(clCxt, &merge_mat, kernelName, globalThreads, localThreads, args, channels, depth); - // } - static void merge_vector_run(const oclMat *mat_src, size_t n, oclMat &mat_dst) { if(!mat_dst.clCxt->supportsFeature(Context::CL_DOUBLE) && mat_dst.type() == CV_64F) @@ -153,10 +98,7 @@ namespace cv int cols = divUp(mat_dst.cols + offset_cols, vector_length); size_t localThreads[3] = { 64, 4, 1 }; - size_t globalThreads[3] = { divUp(cols, localThreads[0]) *localThreads[0], - divUp(mat_dst.rows, localThreads[1]) *localThreads[1], - 1 - }; + size_t globalThreads[3] = { cols, mat_dst.rows, 1 }; int dst_step1 = mat_dst.cols * mat_dst.elemSize(); vector > args; @@ -176,10 +118,6 @@ namespace cv args.push_back( make_pair( sizeof(cl_int), (void *)&mat_src[2].step)); args.push_back( make_pair( sizeof(cl_int), (void *)&mat_src[2].offset)); - // if channel == 3, then the matrix will convert to channel =4 - //if(n == 3) - // args.push_back( make_pair( sizeof(cl_int), (void *)&offset_cols)); - if(n == 3) { args.push_back( make_pair( sizeof(cl_mem), (void *)&mat_src[2].data)); @@ -229,53 +167,6 @@ namespace cv mat_dst.create(size, CV_MAKETYPE(depth, total_channels)); merge_vector_run(mat_src, n, mat_dst); } - //////////////////////////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////split///////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////////////////////////////// - // static void split_vector_run_no_roi(const oclMat &mat_src, oclMat *mat_dst) - // { - // Context *clCxt = mat_src.clCxt; - // int channels = mat_src.oclchannels(); - // int depth = mat_src.depth(); - - // string kernelName = "split_vector"; - - // int indexes[4][7] = {{0, 0, 0, 0, 0, 0, 0}, - // {8, 8, 8, 8, 4, 4, 2}, - // {8, 8, 8, 8 , 4, 4, 4}, - // {4, 4, 2, 2, 1, 1, 1} - // }; - - // size_t index = indexes[channels - 1][mat_dst[0].depth()]; - // int cols = divUp(mat_src.cols, index); - // size_t localThreads[3] = { 64, 4, 1 }; - // size_t globalThreads[3] = { divUp(cols, localThreads[0]) *localThreads[0], - // divUp(mat_src.rows, localThreads[1]) *localThreads[1], - // 1 - // }; - - // vector > args; - // args.push_back( make_pair( sizeof(cl_mem), (void *)&mat_src.data)); - // args.push_back( make_pair( sizeof(cl_int), (void *)&mat_src.step)); - // args.push_back( make_pair( sizeof(cl_int), (void *)&mat_src.rows)); - // args.push_back( make_pair( sizeof(cl_int), (void *)&cols)); - // args.push_back( make_pair( sizeof(cl_mem), (void *)&mat_dst[0].data)); - // args.push_back( make_pair( sizeof(cl_int), (void *)&mat_dst[0].step)); - // args.push_back( make_pair( sizeof(cl_mem), (void *)&mat_dst[1].data)); - // args.push_back( make_pair( sizeof(cl_int), (void *)&mat_dst[1].step)); - // if(channels >= 3) - // { - // args.push_back( make_pair( sizeof(cl_mem), (void *)&mat_dst[2].data)); - // args.push_back( make_pair( sizeof(cl_int), (void *)&mat_dst[2].step)); - // } - // if(channels >= 4) - // { - // args.push_back( make_pair( sizeof(cl_mem), (void *)&mat_dst[3].data)); - // args.push_back( make_pair( sizeof(cl_int), (void *)&mat_dst[3].step)); - // } - - // openCLExecuteKernel(clCxt, &split_mat, kernelName, globalThreads, localThreads, args, channels, depth); - // } static void split_vector_run(const oclMat &mat_src, oclMat *mat_dst) { @@ -311,9 +202,7 @@ namespace cv : divUp(mat_src.cols + max_offset_cols, vector_length); size_t localThreads[3] = { 64, 4, 1 }; - size_t globalThreads[3] = { divUp(cols, localThreads[0]) *localThreads[0], - divUp(mat_src.rows, localThreads[1]) *localThreads[1], 1 - }; + size_t globalThreads[3] = { cols, mat_src.rows, 1 }; int dst_step1 = mat_dst[0].cols * mat_dst[0].elemSize(); vector > args; diff --git a/modules/ocl/src/stereo_csbp.cpp b/modules/ocl/src/stereo_csbp.cpp index 1ae70c07da..b119eadf96 100644 --- a/modules/ocl/src/stereo_csbp.cpp +++ b/modules/ocl/src/stereo_csbp.cpp @@ -96,13 +96,6 @@ namespace cv { namespace stereoCSBP { - ////////////////////////////////////////////////////////////////////////// - //////////////////////////////common//////////////////////////////////// - //////////////////////////////////////////////////////////////////////// - static inline int divUp(int total, int grain) - { - return (total + grain - 1) / grain; - } static string get_kernel_name(string kernel_name, int data_type) { stringstream idxStr; @@ -132,10 +125,7 @@ namespace cv //size_t blockSize = 256; size_t localThreads[] = {32, 8 ,1}; - size_t globalThreads[] = {divUp(w, localThreads[0]) *localThreads[0], - divUp(h, localThreads[1]) *localThreads[1], - 1 - }; + size_t globalThreads[] = { w, h, 1 }; int cdisp_step1 = msg_step * h; openCLVerifyKernel(clCxt, kernel, localThreads); @@ -177,7 +167,7 @@ namespace cv const int threadsNum = 256; //size_t blockSize = threadsNum; size_t localThreads[3] = {win_size, 1, threadsNum / win_size}; - size_t globalThreads[3] = {w *localThreads[0], + size_t globalThreads[3] = { w *localThreads[0], h * divUp(rthis.ndisp, localThreads[2]) *localThreads[1], 1 * localThreads[2] }; @@ -222,10 +212,7 @@ namespace cv //size_t blockSize = 256; size_t localThreads[] = {32, 8 ,1}; - size_t globalThreads[] = {divUp(w, localThreads[0]) *localThreads[0], - divUp(h, localThreads[1]) *localThreads[1], - 1 - }; + size_t globalThreads[] = { w, h, 1 }; int disp_step = msg_step * h; openCLVerifyKernel(clCxt, kernel, localThreads); @@ -257,10 +244,7 @@ namespace cv //size_t blockSize = 256; size_t localThreads[] = {32, 8, 1}; - size_t globalThreads[] = {divUp(w, localThreads[0]) *localThreads[0], - divUp(h, localThreads[1]) *localThreads[1], - 1 - }; + size_t globalThreads[] = { w, h, 1 }; int disp_step = msg_step * h; openCLVerifyKernel(clCxt, kernel, localThreads); @@ -291,14 +275,10 @@ namespace cv init_data_cost_reduce_caller(left, right, temp, rthis, msg_step, h, w, level); if(rthis.use_local_init_data_cost == true) - { get_first_initial_local_caller(data_cost_selected, disp_selected_pyr, temp, rthis, h, w, nr_plane, msg_step); - } else - { get_first_initial_global_caller(data_cost_selected, disp_selected_pyr, temp, rthis, h, w, nr_plane, msg_step); - } } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -317,12 +297,8 @@ namespace cv cl_kernel kernel = openCLGetKernelFromSource(clCxt, &stereocsbp, kernelName); - //size_t blockSize = 256; - size_t localThreads[] = {32, 8, 1}; - size_t globalThreads[] = {divUp(w, localThreads[0]) *localThreads[0], - divUp(h, localThreads[1]) *localThreads[1], - 1 - }; + size_t localThreads[] = { 32, 8, 1 }; + size_t globalThreads[] = { w, h, 1 }; int disp_step1 = msg_step1 * h; int disp_step2 = msg_step2 * h2; @@ -366,8 +342,8 @@ namespace cv const size_t threadsNum = 256; //size_t blockSize = threadsNum; - size_t localThreads[3] = {win_size, 1, threadsNum / win_size}; - size_t globalThreads[3] = {w *localThreads[0], + size_t localThreads[3] = { win_size, 1, threadsNum / win_size }; + size_t globalThreads[3] = { w *localThreads[0], h * divUp(nr_plane, localThreads[2]) *localThreads[1], 1 * localThreads[2] }; @@ -431,10 +407,7 @@ namespace cv //size_t blockSize = 256; size_t localThreads[] = {32, 8, 1}; - size_t globalThreads[] = {divUp(w, localThreads[0]) *localThreads[0], - divUp(h, localThreads[1]) *localThreads[1], - 1 - }; + size_t globalThreads[] = { w, h, 1 }; int disp_step1 = msg_step1 * h; int disp_step2 = msg_step2 * h2; @@ -535,10 +508,7 @@ namespace cv //size_t blockSize = 256; size_t localThreads[] = {32, 8, 1}; - size_t globalThreads[] = {divUp(disp.cols, localThreads[0]) *localThreads[0], - divUp(disp.rows, localThreads[1]) *localThreads[1], - 1 - }; + size_t globalThreads[] = { disp.cols, disp.rows, 1 }; int step_size = disp.step / disp.elemSize(); int disp_step = disp.rows * msg_step; diff --git a/modules/ocl/src/stereobm.cpp b/modules/ocl/src/stereobm.cpp index 151a7eae2f..8195346c00 100644 --- a/modules/ocl/src/stereobm.cpp +++ b/modules/ocl/src/stereobm.cpp @@ -96,10 +96,7 @@ static void prefilter_xsobel(const oclMat &input, oclMat &output, int prefilterC #define N_DISPARITIES 8 #define ROWSperTHREAD 21 #define BLOCK_W 128 -static inline int divUp(int total, int grain) -{ - return (total + grain - 1) / grain; -} + //////////////////////////////////////////////////////////////////////////// ///////////////////////////////stereoBM_GPU//////////////////////////////// //////////////////////////////////////////////////////////////////////////// @@ -117,11 +114,10 @@ static void stereo_bm(const oclMat &left, const oclMat &right, oclMat &disp, size_t local_mem_size = (N_DISPARITIES * (BLOCK_W + 2 * winsz2)) * sizeof(cl_uint); //size_t blockSize = 1; - size_t localThreads[] = { BLOCK_W, 1,1}; - size_t globalThreads[] = { divUp(left.cols - maxdisp - 2 * winsz2, BLOCK_W) *BLOCK_W, + size_t localThreads[] = { BLOCK_W, 1, 1 }; + size_t globalThreads[] = { left.cols - maxdisp - 2 * winsz2, divUp(left.rows - 2 * winsz2, ROWSperTHREAD), - 1 - }; + 1 }; std::vector< std::pair > args; args.push_back(std::make_pair(sizeof(cl_mem), (void *)&left.data)); @@ -151,10 +147,9 @@ static void postfilter_textureness(oclMat &left, int winSize, size_t blockSize = 1; size_t localThreads[] = { BLOCK_W, blockSize ,1}; - size_t globalThreads[] = { divUp(left.cols, BLOCK_W) *BLOCK_W, + size_t globalThreads[] = { left.cols, divUp(left.rows, 2 * ROWSperTHREAD), - 1 - }; + 1 }; size_t local_mem_size = (localThreads[0] + localThreads[0] + (winSize / 2) * 2) * sizeof(float); diff --git a/modules/ocl/src/stereobp.cpp b/modules/ocl/src/stereobp.cpp index 4a326b8146..fe9136057b 100644 --- a/modules/ocl/src/stereobp.cpp +++ b/modules/ocl/src/stereobp.cpp @@ -104,10 +104,7 @@ namespace cv { openCLFree(cl_con_struct); } - static inline int divUp(int total, int grain) - { - return (total + grain - 1) / grain; - } + ///////////////////////////////////////////////////////////////////////////// ///////////////////////////comp data//////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// From 6a26c4483f21a2747c96d6254bfbae873ee0b727 Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Mon, 16 Sep 2013 18:55:37 +0400 Subject: [PATCH 28/68] added performance tests for data transfer operations --- modules/ocl/perf/perf_matrix_operation.cpp | 75 ++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/modules/ocl/perf/perf_matrix_operation.cpp b/modules/ocl/perf/perf_matrix_operation.cpp index ad13275030..13ce47a1bc 100644 --- a/modules/ocl/perf/perf_matrix_operation.cpp +++ b/modules/ocl/perf/perf_matrix_operation.cpp @@ -155,3 +155,78 @@ PERF_TEST_P(setToFixture, setTo, else OCL_PERF_ELSE } + +/////////////////// upload /////////////////////////// + +typedef tuple uploadParams; +typedef TestBaseWithParam uploadFixture; + +PERF_TEST_P(uploadFixture, DISABLED_upload, + testing::Combine( + OCL_TYPICAL_MAT_SIZES, + testing::Range(CV_8U, CV_64F), + testing::Range(1, 5))) +{ + const uploadParams params = GetParam(); + const Size srcSize = get<0>(params); + const int depth = get<1>(params), cn = get<2>(params); + const int type = CV_MAKE_TYPE(depth, cn); + + Mat src(srcSize, type), dst; + declare.in(src, WARMUP_RNG); + + if (RUN_OCL_IMPL) + { + ocl::oclMat oclDst; + + for(; startTimer(), next(); ocl::finish(), stopTimer(), oclDst.release()) + oclDst.upload(src); + } + else if (RUN_PLAIN_IMPL) + { + for(; startTimer(), next(); ocl::finish(), stopTimer(), dst.release()) + dst = src.clone(); + } + else + OCL_PERF_ELSE + + int value = 0; + SANITY_CHECK(value); +} + +/////////////////// download /////////////////////////// + +typedef TestBaseWithParam downloadFixture; + +PERF_TEST_P(downloadFixture, DISABLED_download, + testing::Combine( + OCL_TYPICAL_MAT_SIZES, + testing::Range(CV_8U, CV_64F), + testing::Range(1, 5))) +{ + const uploadParams params = GetParam(); + const Size srcSize = get<0>(params); + const int depth = get<1>(params), cn = get<2>(params); + const int type = CV_MAKE_TYPE(depth, cn); + + Mat src(srcSize, type), dst; + declare.in(src, WARMUP_RNG); + + if (RUN_OCL_IMPL) + { + ocl::oclMat oclSrc(src); + + for(; startTimer(), next(); ocl::finish(), stopTimer(), dst.release()) + oclSrc.download(dst); + } + else if (RUN_PLAIN_IMPL) + { + for(; startTimer(), next(); ocl::finish(), stopTimer(), dst.release()) + dst = src.clone(); + } + else + OCL_PERF_ELSE + + int value = 0; + SANITY_CHECK(value); +} From f1b5cbc8fe734b25220d2b4a1e7156b2017375e5 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Wed, 18 Sep 2013 01:14:41 +0400 Subject: [PATCH 29/68] ocl: fix invalid usage of alignSize (n is not a power of 2), added roundUp --- modules/core/include/opencv2/core/core.hpp | 1 + modules/ocl/src/initialization.cpp | 16 +++++++++++++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/modules/core/include/opencv2/core/core.hpp b/modules/core/include/opencv2/core/core.hpp index 9996c242ad..af3a50c430 100644 --- a/modules/core/include/opencv2/core/core.hpp +++ b/modules/core/include/opencv2/core/core.hpp @@ -339,6 +339,7 @@ template static inline _Tp* alignPtr(_Tp* ptr, int n=(int)sizeof(_ */ static inline size_t alignSize(size_t sz, int n) { + assert((n & (n - 1)) == 0); // n is a power of 2 return (sz + n-1) & -n; } diff --git a/modules/ocl/src/initialization.cpp b/modules/ocl/src/initialization.cpp index 8f5fae3f8d..0e16e75aeb 100644 --- a/modules/ocl/src/initialization.cpp +++ b/modules/ocl/src/initialization.cpp @@ -679,6 +679,16 @@ namespace cv CV_Assert( localThreads[0] * localThreads[1] * localThreads[2] <= clCxt->impl->maxWorkGroupSize ); } + static inline size_t roundUp(size_t sz, size_t n) + { + // we don't assume that n is a power of 2 (see alignSize) + // equal to divUp(sz, n) * n + size_t t = sz + n - 1; + size_t rem = t % n; + size_t result = t - rem; + return result; + } + #ifdef PRINT_KERNEL_RUN_TIME static double total_execute_time = 0; static double total_kernel_time = 0; @@ -702,9 +712,9 @@ namespace cv if ( localThreads != NULL) { - globalThreads[0] = alignSize(globalThreads[0], localThreads[0]); - globalThreads[1] = alignSize(globalThreads[1], localThreads[1]); - globalThreads[2] = alignSize(globalThreads[2], localThreads[2]); + globalThreads[0] = roundUp(globalThreads[0], localThreads[0]); + globalThreads[1] = roundUp(globalThreads[1], localThreads[1]); + globalThreads[2] = roundUp(globalThreads[2], localThreads[2]); cv::ocl::openCLVerifyKernel(clCxt, kernel, localThreads); } From 362a67a6959f19ba27cfda7b3cb296c4d02e35ef Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Fri, 20 Sep 2013 14:22:18 +0400 Subject: [PATCH 30/68] fixed ocl::oclMat::setTo for 2-channel images --- modules/ocl/src/matrix_operations.cpp | 443 ++------------------- modules/ocl/src/opencl/operator_copyToM.cl | 25 +- modules/ocl/src/opencl/operator_setTo.cl | 25 +- modules/ocl/src/opencl/operator_setToM.cl | 29 +- modules/ocl/test/test_matrix_operation.cpp | 143 +++---- 5 files changed, 161 insertions(+), 504 deletions(-) diff --git a/modules/ocl/src/matrix_operations.cpp b/modules/ocl/src/matrix_operations.cpp index 61b6df896f..ff52b8a55b 100644 --- a/modules/ocl/src/matrix_operations.cpp +++ b/modules/ocl/src/matrix_operations.cpp @@ -445,200 +445,62 @@ void cv::ocl::oclMat::convertTo( oclMat &dst, int rtype, double alpha, double be /////////////////////////////////////////////////////////////////////////// //////////////////////////////// setTo //////////////////////////////////// /////////////////////////////////////////////////////////////////////////// + oclMat &cv::ocl::oclMat::operator = (const Scalar &s) { setTo(s); return *this; } + static void set_to_withoutmask_run(const oclMat &dst, const Scalar &scalar, string kernelName) { vector > args; size_t localThreads[3] = {16, 16, 1}; - size_t globalThreads[3]; - globalThreads[0] = (dst.cols + localThreads[0] - 1) / localThreads[0] * localThreads[0]; - globalThreads[1] = (dst.rows + localThreads[1] - 1) / localThreads[1] * localThreads[1]; - globalThreads[2] = 1; + size_t globalThreads[3] = { dst.cols, dst.rows, 1 }; int step_in_pixel = dst.step / dst.elemSize(), offset_in_pixel = dst.offset / dst.elemSize(); - if(dst.type() == CV_8UC1) - { + + if (dst.type() == CV_8UC1) globalThreads[0] = ((dst.cols + 4) / 4 + localThreads[0] - 1) / localThreads[0] * localThreads[0]; - } - char compile_option[32]; - union sc - { - cl_uchar4 uval; - cl_char4 cval; - cl_ushort4 usval; - cl_short4 shval; - cl_int4 ival; - cl_float4 fval; - cl_double4 dval; - } val; - switch(dst.depth()) - { - case CV_8U: - val.uval.s[0] = saturate_cast(scalar.val[0]); - val.uval.s[1] = saturate_cast(scalar.val[1]); - val.uval.s[2] = saturate_cast(scalar.val[2]); - val.uval.s[3] = saturate_cast(scalar.val[3]); - switch(dst.oclchannels()) - { - case 1: - sprintf(compile_option, "-D GENTYPE=uchar"); - args.push_back( make_pair( sizeof(cl_uchar) , (void *)&val.uval.s[0] )); - break; - case 4: - sprintf(compile_option, "-D GENTYPE=uchar4"); - args.push_back( make_pair( sizeof(cl_uchar4) , (void *)&val.uval )); - break; - default: - CV_Error(CV_StsUnsupportedFormat, "unsupported channels"); - } - break; - case CV_8S: - val.cval.s[0] = saturate_cast(scalar.val[0]); - val.cval.s[1] = saturate_cast(scalar.val[1]); - val.cval.s[2] = saturate_cast(scalar.val[2]); - val.cval.s[3] = saturate_cast(scalar.val[3]); - switch(dst.oclchannels()) - { - case 1: - sprintf(compile_option, "-D GENTYPE=char"); - args.push_back( make_pair( sizeof(cl_char) , (void *)&val.cval.s[0] )); - break; - case 4: - sprintf(compile_option, "-D GENTYPE=char4"); - args.push_back( make_pair( sizeof(cl_char4) , (void *)&val.cval )); - break; - default: - CV_Error(CV_StsUnsupportedFormat, "unsupported channels"); - } - break; - case CV_16U: - val.usval.s[0] = saturate_cast(scalar.val[0]); - val.usval.s[1] = saturate_cast(scalar.val[1]); - val.usval.s[2] = saturate_cast(scalar.val[2]); - val.usval.s[3] = saturate_cast(scalar.val[3]); - switch(dst.oclchannels()) - { - case 1: - sprintf(compile_option, "-D GENTYPE=ushort"); - args.push_back( make_pair( sizeof(cl_ushort) , (void *)&val.usval.s[0] )); - break; - case 4: - sprintf(compile_option, "-D GENTYPE=ushort4"); - args.push_back( make_pair( sizeof(cl_ushort4) , (void *)&val.usval )); - break; - default: - CV_Error(CV_StsUnsupportedFormat, "unsupported channels"); - } - break; - case CV_16S: - val.shval.s[0] = saturate_cast(scalar.val[0]); - val.shval.s[1] = saturate_cast(scalar.val[1]); - val.shval.s[2] = saturate_cast(scalar.val[2]); - val.shval.s[3] = saturate_cast(scalar.val[3]); - switch(dst.oclchannels()) - { - case 1: - sprintf(compile_option, "-D GENTYPE=short"); - args.push_back( make_pair( sizeof(cl_short) , (void *)&val.shval.s[0] )); - break; - case 4: - sprintf(compile_option, "-D GENTYPE=short4"); - args.push_back( make_pair( sizeof(cl_short4) , (void *)&val.shval )); - break; - default: - CV_Error(CV_StsUnsupportedFormat, "unsupported channels"); - } - break; - case CV_32S: - val.ival.s[0] = saturate_cast(scalar.val[0]); - val.ival.s[1] = saturate_cast(scalar.val[1]); - val.ival.s[2] = saturate_cast(scalar.val[2]); - val.ival.s[3] = saturate_cast(scalar.val[3]); - switch(dst.oclchannels()) - { - case 1: - sprintf(compile_option, "-D GENTYPE=int"); - args.push_back( make_pair( sizeof(cl_int) , (void *)&val.ival.s[0] )); - break; - case 2: - sprintf(compile_option, "-D GENTYPE=int2"); - cl_int2 i2val; - i2val.s[0] = val.ival.s[0]; - i2val.s[1] = val.ival.s[1]; - args.push_back( make_pair( sizeof(cl_int2) , (void *)&i2val )); - break; - case 4: - sprintf(compile_option, "-D GENTYPE=int4"); - args.push_back( make_pair( sizeof(cl_int4) , (void *)&val.ival )); - break; - default: - CV_Error(CV_StsUnsupportedFormat, "unsupported channels"); - } - break; - case CV_32F: - val.fval.s[0] = scalar.val[0]; - val.fval.s[1] = scalar.val[1]; - val.fval.s[2] = scalar.val[2]; - val.fval.s[3] = scalar.val[3]; - switch(dst.oclchannels()) - { - case 1: - sprintf(compile_option, "-D GENTYPE=float"); - args.push_back( make_pair( sizeof(cl_float) , (void *)&val.fval.s[0] )); - break; - case 4: - sprintf(compile_option, "-D GENTYPE=float4"); - args.push_back( make_pair( sizeof(cl_float4) , (void *)&val.fval )); - break; - default: - CV_Error(CV_StsUnsupportedFormat, "unsupported channels"); - } - break; - case CV_64F: - val.dval.s[0] = scalar.val[0]; - val.dval.s[1] = scalar.val[1]; - val.dval.s[2] = scalar.val[2]; - val.dval.s[3] = scalar.val[3]; - switch(dst.oclchannels()) - { - case 1: - sprintf(compile_option, "-D GENTYPE=double"); - args.push_back( make_pair( sizeof(cl_double) , (void *)&val.dval.s[0] )); - break; - case 4: - sprintf(compile_option, "-D GENTYPE=double4"); - args.push_back( make_pair( sizeof(cl_double4) , (void *)&val.dval )); - break; - default: - CV_Error(CV_StsUnsupportedFormat, "unsupported channels"); - } - break; - default: - CV_Error(CV_StsUnsupportedFormat, "unknown depth"); - } + + const char * const typeMap[] = { "uchar", "char", "ushort", "short", "int", "float", "double" }; + const char channelMap[] = { ' ', ' ', '2', '4', '4' }; + std::string buildOptions = format("-D GENTYPE=%s%c", typeMap[dst.depth()], channelMap[dst.channels()]); + + Mat mat(1, 1, dst.type(), scalar); + #ifdef CL_VERSION_1_2 - //this enables backwards portability to - //run on OpenCL 1.1 platform if library binaries are compiled with OpenCL 1.2 support - if(Context::getContext()->supportsFeature(Context::CL_VER_1_2) && + // this enables backwards portability to + // run on OpenCL 1.1 platform if library binaries are compiled with OpenCL 1.2 support + if (Context::getContext()->supportsFeature(Context::CL_VER_1_2) && dst.offset == 0 && dst.cols == dst.wholecols) { + const int sizeofMap[][7] = + { + { sizeof(cl_uchar) , sizeof(cl_char) , sizeof(cl_ushort) , sizeof(cl_short) , sizeof(cl_int) , sizeof(cl_float) , sizeof(cl_double) }, + { sizeof(cl_uchar2), sizeof(cl_char2), sizeof(cl_ushort2), sizeof(cl_short2), sizeof(cl_int2), sizeof(cl_float2), sizeof(cl_double2) }, + { 0 , 0 , 0 , 0 , 0 , 0 , 0 }, + { sizeof(cl_uchar4), sizeof(cl_char4), sizeof(cl_ushort4), sizeof(cl_short4), sizeof(cl_int4), sizeof(cl_float4), sizeof(cl_double4) }, + }; + int sizeofGeneric = sizeofMap[dst.oclchannels() - 1][dst.depth()]; + clEnqueueFillBuffer((cl_command_queue)dst.clCxt->oclCommandQueue(), - (cl_mem)dst.data, args[0].second, args[0].first, 0, dst.step * dst.rows, 0, NULL, NULL); + (cl_mem)dst.data, (void*)mat.data, sizeofGeneric, + 0, dst.step * dst.rows, 0, NULL, NULL); } else #endif { + oclMat m(mat); + args.push_back( make_pair( sizeof(cl_mem) , (void*)&m.data )); args.push_back( make_pair( sizeof(cl_mem) , (void *)&dst.data )); args.push_back( make_pair( sizeof(cl_int) , (void *)&dst.cols )); args.push_back( make_pair( sizeof(cl_int) , (void *)&dst.rows )); args.push_back( make_pair( sizeof(cl_int) , (void *)&step_in_pixel )); - args.push_back( make_pair( sizeof(cl_int) , (void *)&offset_in_pixel)); + args.push_back( make_pair( sizeof(cl_int) , (void *)&offset_in_pixel )); + openCLExecuteKernel(dst.clCxt , &operator_setTo, kernelName, globalThreads, - localThreads, args, -1, -1, compile_option); + localThreads, args, -1, -1, buildOptions.c_str()); } } @@ -646,161 +508,16 @@ static void set_to_withmask_run(const oclMat &dst, const Scalar &scalar, const o { CV_DbgAssert( dst.rows == mask.rows && dst.cols == mask.cols); vector > args; - size_t localThreads[3] = {16, 16, 1}; - size_t globalThreads[3]; - globalThreads[0] = (dst.cols + localThreads[0] - 1) / localThreads[0] * localThreads[0]; - globalThreads[1] = (dst.rows + localThreads[1] - 1) / localThreads[1] * localThreads[1]; - globalThreads[2] = 1; + size_t localThreads[3] = { 16, 16, 1 }; + size_t globalThreads[3] = { dst.cols, dst.rows, 1 }; int step_in_pixel = dst.step / dst.elemSize(), offset_in_pixel = dst.offset / dst.elemSize(); - char compile_option[32]; - union sc - { - cl_uchar4 uval; - cl_char4 cval; - cl_ushort4 usval; - cl_short4 shval; - cl_int4 ival; - cl_float4 fval; - cl_double4 dval; - } val; - switch(dst.depth()) - { - case CV_8U: - val.uval.s[0] = saturate_cast(scalar.val[0]); - val.uval.s[1] = saturate_cast(scalar.val[1]); - val.uval.s[2] = saturate_cast(scalar.val[2]); - val.uval.s[3] = saturate_cast(scalar.val[3]); - switch(dst.oclchannels()) - { - case 1: - sprintf(compile_option, "-D GENTYPE=uchar"); - args.push_back( make_pair( sizeof(cl_uchar) , (void *)&val.uval.s[0] )); - break; - case 4: - sprintf(compile_option, "-D GENTYPE=uchar4"); - args.push_back( make_pair( sizeof(cl_uchar4) , (void *)&val.uval )); - break; - default: - CV_Error(CV_StsUnsupportedFormat, "unsupported channels"); - } - break; - case CV_8S: - val.cval.s[0] = saturate_cast(scalar.val[0]); - val.cval.s[1] = saturate_cast(scalar.val[1]); - val.cval.s[2] = saturate_cast(scalar.val[2]); - val.cval.s[3] = saturate_cast(scalar.val[3]); - switch(dst.oclchannels()) - { - case 1: - sprintf(compile_option, "-D GENTYPE=char"); - args.push_back( make_pair( sizeof(cl_char) , (void *)&val.cval.s[0] )); - break; - case 4: - sprintf(compile_option, "-D GENTYPE=char4"); - args.push_back( make_pair( sizeof(cl_char4) , (void *)&val.cval )); - break; - default: - CV_Error(CV_StsUnsupportedFormat, "unsupported channels"); - } - break; - case CV_16U: - val.usval.s[0] = saturate_cast(scalar.val[0]); - val.usval.s[1] = saturate_cast(scalar.val[1]); - val.usval.s[2] = saturate_cast(scalar.val[2]); - val.usval.s[3] = saturate_cast(scalar.val[3]); - switch(dst.oclchannels()) - { - case 1: - sprintf(compile_option, "-D GENTYPE=ushort"); - args.push_back( make_pair( sizeof(cl_ushort) , (void *)&val.usval.s[0] )); - break; - case 4: - sprintf(compile_option, "-D GENTYPE=ushort4"); - args.push_back( make_pair( sizeof(cl_ushort4) , (void *)&val.usval )); - break; - default: - CV_Error(CV_StsUnsupportedFormat, "unsupported channels"); - } - break; - case CV_16S: - val.shval.s[0] = saturate_cast(scalar.val[0]); - val.shval.s[1] = saturate_cast(scalar.val[1]); - val.shval.s[2] = saturate_cast(scalar.val[2]); - val.shval.s[3] = saturate_cast(scalar.val[3]); - switch(dst.oclchannels()) - { - case 1: - sprintf(compile_option, "-D GENTYPE=short"); - args.push_back( make_pair( sizeof(cl_short) , (void *)&val.shval.s[0] )); - break; - case 4: - sprintf(compile_option, "-D GENTYPE=short4"); - args.push_back( make_pair( sizeof(cl_short4) , (void *)&val.shval )); - break; - default: - CV_Error(CV_StsUnsupportedFormat, "unsupported channels"); - } - break; - case CV_32S: - val.ival.s[0] = saturate_cast(scalar.val[0]); - val.ival.s[1] = saturate_cast(scalar.val[1]); - val.ival.s[2] = saturate_cast(scalar.val[2]); - val.ival.s[3] = saturate_cast(scalar.val[3]); - switch(dst.oclchannels()) - { - case 1: - sprintf(compile_option, "-D GENTYPE=int"); - args.push_back( make_pair( sizeof(cl_int) , (void *)&val.ival.s[0] )); - break; - case 4: - sprintf(compile_option, "-D GENTYPE=int4"); - args.push_back( make_pair( sizeof(cl_int4) , (void *)&val.ival )); - break; - default: - CV_Error(CV_StsUnsupportedFormat, "unsupported channels"); - } - break; - case CV_32F: - val.fval.s[0] = scalar.val[0]; - val.fval.s[1] = scalar.val[1]; - val.fval.s[2] = scalar.val[2]; - val.fval.s[3] = scalar.val[3]; - switch(dst.oclchannels()) - { - case 1: - sprintf(compile_option, "-D GENTYPE=float"); - args.push_back( make_pair( sizeof(cl_float) , (void *)&val.fval.s[0] )); - break; - case 4: - sprintf(compile_option, "-D GENTYPE=float4"); - args.push_back( make_pair( sizeof(cl_float4) , (void *)&val.fval )); - break; - default: - CV_Error(CV_StsUnsupportedFormat, "unsupported channels"); - } - break; - case CV_64F: - val.dval.s[0] = scalar.val[0]; - val.dval.s[1] = scalar.val[1]; - val.dval.s[2] = scalar.val[2]; - val.dval.s[3] = scalar.val[3]; - switch(dst.oclchannels()) - { - case 1: - sprintf(compile_option, "-D GENTYPE=double"); - args.push_back( make_pair( sizeof(cl_double) , (void *)&val.dval.s[0] )); - break; - case 4: - sprintf(compile_option, "-D GENTYPE=double4"); - args.push_back( make_pair( sizeof(cl_double4) , (void *)&val.dval )); - break; - default: - CV_Error(CV_StsUnsupportedFormat, "unsupported channels"); - } - break; - default: - CV_Error(CV_StsUnsupportedFormat, "unknown depth"); - } + + const char * const typeMap[] = { "uchar", "char", "ushort", "short", "int", "float", "double" }; + const char channelMap[] = { ' ', ' ', '2', '4', '4' }; + std::string buildOptions = format("-D GENTYPE=%s%c", typeMap[dst.depth()], channelMap[dst.channels()]); + + oclMat m(Mat(1, 1, dst.type(), scalar)); + args.push_back( make_pair( sizeof(cl_mem) , (void *)&m.data )); args.push_back( make_pair( sizeof(cl_mem) , (void *)&dst.data )); args.push_back( make_pair( sizeof(cl_int) , (void *)&dst.cols )); args.push_back( make_pair( sizeof(cl_int) , (void *)&dst.rows )); @@ -810,38 +527,21 @@ static void set_to_withmask_run(const oclMat &dst, const Scalar &scalar, const o args.push_back( make_pair( sizeof(cl_int) , (void *)&mask.step )); args.push_back( make_pair( sizeof(cl_int) , (void *)&mask.offset )); openCLExecuteKernel(dst.clCxt , &operator_setToM, kernelName, globalThreads, - localThreads, args, -1, -1, compile_option); + localThreads, args, -1, -1, buildOptions.c_str()); } oclMat &cv::ocl::oclMat::setTo(const Scalar &scalar, const oclMat &mask) { - //cout << "cv::ocl::oclMat::setTo()" << endl; CV_Assert(mask.type() == CV_8UC1); CV_Assert( this->depth() >= 0 && this->depth() <= 6 ); CV_DbgAssert( !this->empty()); - //cl_int status; - //cl_mem mem; - //mem = clCreateBuffer(this->clCxt->clContext,CL_MEM_READ_WRITE, - // sizeof(double)*4,NULL,&status); - //openCLVerifyCall(status); - //double* s = (double *)scalar.val; - //openCLSafeCall(clEnqueueWriteBuffer(this->clCxt->clCmdQueue, - // (cl_mem)mem,1,0,sizeof(double)*4,s,0,0,0)); if (mask.empty()) { - if(type() == CV_8UC1) - { - set_to_withoutmask_run(*this, scalar, "set_to_without_mask_C1_D0"); - } - else - { - set_to_withoutmask_run(*this, scalar, "set_to_without_mask"); - } + set_to_withoutmask_run(*this, scalar, type() == CV_8UC1 ? + "set_to_without_mask_C1_D0" : "set_to_without_mask"); } else - { set_to_withmask_run(*this, scalar, mask, "set_to_with_mask"); - } return *this; } @@ -849,91 +549,43 @@ oclMat &cv::ocl::oclMat::setTo(const Scalar &scalar, const oclMat &mask) oclMat cv::ocl::oclMat::reshape(int new_cn, int new_rows) const { if( new_rows != 0 && new_rows != rows) - - { - - CV_Error( CV_StsBadFunc, - - "oclMat's number of rows can not be changed for current version" ); - - } + CV_Error( CV_StsBadFunc, "oclMat's number of rows can not be changed for current version" ); oclMat hdr = *this; int cn = oclchannels(); - if (new_cn == 0) - new_cn = cn; - - int total_width = cols * cn; - - - if ((new_cn > total_width || total_width % new_cn != 0) && new_rows == 0) - new_rows = rows * total_width / new_cn; - - if (new_rows != 0 && new_rows != rows) - { - int total_size = total_width * rows; - - if (!isContinuous()) - CV_Error(CV_BadStep, "The matrix is not continuous, thus its number of rows can not be changed"); - - if ((unsigned)new_rows > (unsigned)total_size) - CV_Error(CV_StsOutOfRange, "Bad new number of rows"); - - total_width = total_size / new_rows; - - - if (total_width * new_rows != total_size) - CV_Error(CV_StsBadArg, "The total number of matrix elements is not divisible by the new number of rows"); - - hdr.rows = new_rows; - hdr.step = total_width * elemSize1(); - } - - int new_width = total_width / new_cn; - - - if (new_width * new_cn != total_width) - CV_Error(CV_BadNumChannels, "The total width is not divisible by the new number of channels"); - - hdr.cols = new_width; - hdr.wholecols = new_width; - hdr.flags = (hdr.flags & ~CV_MAT_CN_MASK) | ((new_cn - 1) << CV_CN_SHIFT); - - - return hdr; } @@ -953,11 +605,6 @@ void cv::ocl::oclMat::createEx(int _rows, int _cols, int _type, DevMemRW rw_type clCxt = Context::getContext(); /* core logic */ _type &= TYPE_MASK; - //download_channels = CV_MAT_CN(_type); - //if(download_channels==3) - //{ - // _type = CV_MAKE_TYPE((CV_MAT_DEPTH(_type)),4); - //} if( rows == _rows && cols == _cols && type() == _type && data ) return; if( data ) @@ -974,7 +621,6 @@ void cv::ocl::oclMat::createEx(int _rows, int _cols, int _type, DevMemRW rw_type void *dev_ptr; openCLMallocPitchEx(clCxt, &dev_ptr, &step, GPU_MATRIX_MALLOC_STEP(esz * cols), rows, rw_type, mem_type); - //openCLMallocPitch(clCxt,&dev_ptr, &step, esz * cols, rows); if (esz * cols == step) flags |= Mat::CONTINUOUS_FLAG; @@ -992,7 +638,6 @@ void cv::ocl::oclMat::createEx(int _rows, int _cols, int _type, DevMemRW rw_type void cv::ocl::oclMat::release() { - //cout << "cv::ocl::oclMat::release()" << endl; if( refcount && CV_XADD(refcount, -1) == 1 ) { fastFree(refcount); diff --git a/modules/ocl/src/opencl/operator_copyToM.cl b/modules/ocl/src/opencl/operator_copyToM.cl index c49c6a3230..69b5ea4ab4 100644 --- a/modules/ocl/src/opencl/operator_copyToM.cl +++ b/modules/ocl/src/opencl/operator_copyToM.cl @@ -34,6 +34,14 @@ // // +#if defined (DOUBLE_SUPPORT) +#ifdef cl_khr_fp64 +#pragma OPENCL EXTENSION cl_khr_fp64:enable +#elif defined (cl_amd_fp64) +#pragma OPENCL EXTENSION cl_amd_fp64:enable +#endif +#endif + __kernel void copy_to_with_mask( __global const GENTYPE* restrict srcMat, __global GENTYPE* dstMat, @@ -47,16 +55,17 @@ __kernel void copy_to_with_mask( int maskStep, int maskoffset) { - int x=get_global_id(0); - int y=get_global_id(1); - x = x< cols ? x: cols-1; - y = y< rows ? y: rows-1; - int srcidx = mad24(y,srcStep_in_pixel,x+ srcoffset_in_pixel); - int dstidx = mad24(y,dstStep_in_pixel,x+ dstoffset_in_pixel); + int x=get_global_id(0); + int y=get_global_id(1); + + if (x < cols && y < rows) + { int maskidx = mad24(y,maskStep,x+ maskoffset); - uchar mask = maskMat[maskidx]; - if (mask) + if ( maskMat[maskidx]) { + int srcidx = mad24(y,srcStep_in_pixel,x+ srcoffset_in_pixel); + int dstidx = mad24(y,dstStep_in_pixel,x+ dstoffset_in_pixel); dstMat[dstidx] = srcMat[srcidx]; } + } } diff --git a/modules/ocl/src/opencl/operator_setTo.cl b/modules/ocl/src/opencl/operator_setTo.cl index 0075dc5b52..1d2ad65977 100644 --- a/modules/ocl/src/opencl/operator_setTo.cl +++ b/modules/ocl/src/opencl/operator_setTo.cl @@ -34,17 +34,22 @@ // // +#if defined (DOUBLE_SUPPORT) +#ifdef cl_khr_fp64 +#pragma OPENCL EXTENSION cl_khr_fp64:enable +#elif defined (cl_amd_fp64) +#pragma OPENCL EXTENSION cl_amd_fp64:enable +#endif +#endif -__kernel void set_to_without_mask_C1_D0(uchar scalar,__global uchar * dstMat, +__kernel void set_to_without_mask_C1_D0(__global uchar * scalar,__global uchar * dstMat, int cols,int rows,int dstStep_in_pixel,int offset_in_pixel) { int x=get_global_id(0)<<2; int y=get_global_id(1); - //int addr_start = mad24(y,dstStep_in_pixel,offset_in_pixel); - //int addr_end = mad24(y,dstStep_in_pixel,cols+offset_in_pixel); int idx = mad24(y,dstStep_in_pixel,x+ offset_in_pixel); uchar4 out; - out.x = out.y = out.z = out.w = scalar; + out.x = out.y = out.z = out.w = scalar[0]; if ( (x+3 < cols) && (y < rows)&& ((offset_in_pixel&3) == 0)) { @@ -77,14 +82,14 @@ __kernel void set_to_without_mask_C1_D0(uchar scalar,__global uchar * dstMat, } } -__kernel void set_to_without_mask(GENTYPE scalar,__global GENTYPE * dstMat, - int cols,int rows,int dstStep_in_pixel,int offset_in_pixel) +__kernel void set_to_without_mask(__global GENTYPE * scalar,__global GENTYPE * dstMat, + int cols, int rows, int dstStep_in_pixel, int offset_in_pixel) { - int x=get_global_id(0); - int y=get_global_id(1); + int x = get_global_id(0); + int y = get_global_id(1); if ( (x < cols) & (y < rows)) { - int idx = mad24(y,dstStep_in_pixel,x+ offset_in_pixel); - dstMat[idx] = scalar; + int idx = mad24(y, dstStep_in_pixel, x + offset_in_pixel); + dstMat[idx] = scalar[0]; } } diff --git a/modules/ocl/src/opencl/operator_setToM.cl b/modules/ocl/src/opencl/operator_setToM.cl index dde12d86f6..a1cb092f87 100644 --- a/modules/ocl/src/opencl/operator_setToM.cl +++ b/modules/ocl/src/opencl/operator_setToM.cl @@ -33,8 +33,17 @@ // the use of this software, even if advised of the possibility of such damage. // // + +#if defined (DOUBLE_SUPPORT) +#ifdef cl_khr_fp64 +#pragma OPENCL EXTENSION cl_khr_fp64:enable +#elif defined (cl_amd_fp64) +#pragma OPENCL EXTENSION cl_amd_fp64:enable +#endif +#endif + __kernel void set_to_with_mask( - GENTYPE scalar, + __global GENTYPE * scalar, __global GENTYPE * dstMat, int cols, int rows, @@ -44,16 +53,16 @@ __kernel void set_to_with_mask( int maskStep, int maskoffset) { - int x=get_global_id(0); - int y=get_global_id(1); - x = x< cols ? x: cols-1; - y = y< rows ? y: rows-1; - int dstidx = mad24(y,dstStep_in_pixel,x+ dstoffset_in_pixel); + int x = get_global_id(0); + int y = get_global_id(1); + + if (x < cols && y < rows) + { int maskidx = mad24(y,maskStep,x+ maskoffset); - uchar mask = maskMat[maskidx]; - if (mask) + if (maskMat[maskidx]) { - dstMat[dstidx] = scalar; + int dstidx = mad24(y,dstStep_in_pixel,x+ dstoffset_in_pixel); + dstMat[dstidx] = scalar[0]; } - + } } diff --git a/modules/ocl/test/test_matrix_operation.cpp b/modules/ocl/test/test_matrix_operation.cpp index c5fcdce2c0..b70ee6ccd9 100644 --- a/modules/ocl/test/test_matrix_operation.cpp +++ b/modules/ocl/test/test_matrix_operation.cpp @@ -77,7 +77,7 @@ PARAM_TEST_CASE(ConvertToTestBase, MatType, MatType, int, bool) cv::ocl::oclMat gdst_whole; // ocl mat with roi - cv::ocl::oclMat gmat; + cv::ocl::oclMat gsrc; cv::ocl::oclMat gdst; virtual void SetUp() @@ -123,7 +123,7 @@ PARAM_TEST_CASE(ConvertToTestBase, MatType, MatType, int, bool) gdst_whole = dst; gdst = gdst_whole(Rect(dstx, dsty, roicols, roirows)); - gmat = mat_roi; + gsrc = mat_roi; } }; @@ -136,7 +136,7 @@ TEST_P(ConvertTo, Accuracy) random_roi(); mat_roi.convertTo(dst_roi, dst_type); - gmat.convertTo(gdst, dst_type); + gsrc.convertTo(gdst, dst_type); EXPECT_MAT_NEAR(dst, Mat(gdst_whole), src_depth == CV_64F ? 1.0 : 0.0); EXPECT_MAT_NEAR(dst_roi, Mat(gdst), src_depth == CV_64F ? 1.0 : 0.0); @@ -145,27 +145,20 @@ TEST_P(ConvertTo, Accuracy) ///////////////////////////////////////////copyto///////////////////////////////////////////////////////////// -PARAM_TEST_CASE(CopyToTestBase, MatType, bool) +PARAM_TEST_CASE(CopyToTestBase, MatType, int, bool) { - int type; bool use_roi; - cv::Mat mat; - cv::Mat mask; - cv::Mat dst; + cv::Mat src, mask, dst; // set up roi - int roicols; - int roirows; - int srcx; - int srcy; - int dstx; - int dsty; - int maskx; - int masky; + int roicols,roirows; + int srcx, srcy; + int dstx, dsty; + int maskx,masky; // src mat with roi - cv::Mat mat_roi; + cv::Mat src_roi; cv::Mat mask_roi; cv::Mat dst_roi; @@ -173,21 +166,18 @@ PARAM_TEST_CASE(CopyToTestBase, MatType, bool) cv::ocl::oclMat gdst_whole; // ocl mat with roi - cv::ocl::oclMat gmat; - cv::ocl::oclMat gdst; - cv::ocl::oclMat gmask; + cv::ocl::oclMat gsrc, gdst, gmask; virtual void SetUp() { - type = GET_PARAM(0); - use_roi = GET_PARAM(1); + int type = CV_MAKETYPE(GET_PARAM(0), GET_PARAM(1)); + use_roi = GET_PARAM(2); cv::RNG &rng = TS::ptr()->get_rng(); - cv::Size size(MWIDTH, MHEIGHT); - mat = randomMat(rng, size, type, 5, 16, false); - dst = randomMat(rng, size, type, 5, 16, false); - mask = randomMat(rng, size, CV_8UC1, 0, 2, false); + src = randomMat(rng, randomSize(MIN_VALUE, MAX_VALUE), type, 5, 16, false); + dst = randomMat(rng, use_roi ? randomSize(MIN_VALUE, MAX_VALUE) : src.size(), type, 5, 16, false); + mask = randomMat(rng, use_roi ? randomSize(MIN_VALUE, MAX_VALUE) : src.size(), CV_8UC1, 0, 2, false); cv::threshold(mask, mask, 0.5, 255., CV_8UC1); } @@ -198,32 +188,32 @@ PARAM_TEST_CASE(CopyToTestBase, MatType, bool) { // randomize ROI cv::RNG &rng = TS::ptr()->get_rng(); - roicols = rng.uniform(1, mat.cols); - roirows = rng.uniform(1, mat.rows); - srcx = rng.uniform(0, mat.cols - roicols); - srcy = rng.uniform(0, mat.rows - roirows); - dstx = rng.uniform(0, dst.cols - roicols); - dsty = rng.uniform(0, dst.rows - roirows); + roicols = rng.uniform(1, MIN_VALUE); + roirows = rng.uniform(1, MIN_VALUE); + srcx = rng.uniform(0, src.cols - roicols); + srcy = rng.uniform(0, src.rows - roirows); + dstx = rng.uniform(0, dst.cols - roicols); + dsty = rng.uniform(0, dst.rows - roirows); maskx = rng.uniform(0, mask.cols - roicols); masky = rng.uniform(0, mask.rows - roirows); } else { - roicols = mat.cols; - roirows = mat.rows; + roicols = src.cols; + roirows = src.rows; srcx = srcy = 0; dstx = dsty = 0; maskx = masky = 0; } - mat_roi = mat(Rect(srcx, srcy, roicols, roirows)); + src_roi = src(Rect(srcx, srcy, roicols, roirows)); mask_roi = mask(Rect(maskx, masky, roicols, roirows)); dst_roi = dst(Rect(dstx, dsty, roicols, roirows)); gdst_whole = dst; gdst = gdst_whole(Rect(dstx, dsty, roicols, roirows)); - gmat = mat_roi; + gsrc = src_roi; gmask = mask_roi; } }; @@ -236,8 +226,8 @@ TEST_P(CopyTo, Without_mask) { random_roi(); - mat_roi.copyTo(dst_roi); - gmat.copyTo(gdst); + src_roi.copyTo(dst_roi); + gsrc.copyTo(gdst); EXPECT_MAT_NEAR(dst, Mat(gdst_whole), 0.0); } @@ -249,8 +239,8 @@ TEST_P(CopyTo, With_mask) { random_roi(); - mat_roi.copyTo(dst_roi, mask_roi); - gmat.copyTo(gdst, gmask); + src_roi.copyTo(dst_roi, mask_roi); + gsrc.copyTo(gdst, gmask); EXPECT_MAT_NEAR(dst, Mat(gdst_whole), 0.0); } @@ -258,48 +248,47 @@ TEST_P(CopyTo, With_mask) /////////////////////////////////////////// setTo ///////////////////////////////////////////////////////////// -PARAM_TEST_CASE(SetToTestBase, MatType, bool) +PARAM_TEST_CASE(SetToTestBase, MatType, int, bool) { - int type; + int depth, channels; bool use_roi; cv::Scalar val; - cv::Mat mat; + cv::Mat src; cv::Mat mask; // set up roi - int roicols; - int roirows; - int srcx; - int srcy; - int maskx; - int masky; + int roicols, roirows; + int srcx, srcy; + int maskx, masky; // src mat with roi - cv::Mat mat_roi; + cv::Mat src_roi; cv::Mat mask_roi; // ocl dst mat for testing - cv::ocl::oclMat gmat_whole; + cv::ocl::oclMat gsrc_whole; // ocl mat with roi - cv::ocl::oclMat gmat; + cv::ocl::oclMat gsrc; cv::ocl::oclMat gmask; virtual void SetUp() { - type = GET_PARAM(0); - use_roi = GET_PARAM(1); + depth = GET_PARAM(0); + channels = GET_PARAM(1); + use_roi = GET_PARAM(2); cv::RNG &rng = TS::ptr()->get_rng(); - cv::Size size(MWIDTH, MHEIGHT); + int type = CV_MAKE_TYPE(depth, channels); - mat = randomMat(rng, size, type, 5, 16, false); - mask = randomMat(rng, size, CV_8UC1, 0, 2, false); + src = randomMat(rng, randomSize(MIN_VALUE, MAX_VALUE), type, 5, 16, false); + mask = randomMat(rng, use_roi ? randomSize(MIN_VALUE, MAX_VALUE) : src.size(), CV_8UC1, 0, 2, false); cv::threshold(mask, mask, 0.5, 255., CV_8UC1); - val = cv::Scalar(rng.uniform(-10.0, 10.0), rng.uniform(-10.0, 10.0), rng.uniform(-10.0, 10.0), rng.uniform(-10.0, 10.0)); + val = cv::Scalar(rng.uniform(-10.0, 10.0), rng.uniform(-10.0, 10.0), + rng.uniform(-10.0, 10.0), rng.uniform(-10.0, 10.0)); } void random_roi() @@ -308,26 +297,26 @@ PARAM_TEST_CASE(SetToTestBase, MatType, bool) { // randomize ROI cv::RNG &rng = TS::ptr()->get_rng(); - roicols = rng.uniform(1, mat.cols); - roirows = rng.uniform(1, mat.rows); - srcx = rng.uniform(0, mat.cols - roicols); - srcy = rng.uniform(0, mat.rows - roirows); + roicols = rng.uniform(1, MIN_VALUE); + roirows = rng.uniform(1, MIN_VALUE); + srcx = rng.uniform(0, src.cols - roicols); + srcy = rng.uniform(0, src.rows - roirows); maskx = rng.uniform(0, mask.cols - roicols); masky = rng.uniform(0, mask.rows - roirows); } else { - roicols = mat.cols; - roirows = mat.rows; + roicols = src.cols; + roirows = src.rows; srcx = srcy = 0; maskx = masky = 0; } - mat_roi = mat(Rect(srcx, srcy, roicols, roirows)); + src_roi = src(Rect(srcx, srcy, roicols, roirows)); mask_roi = mask(Rect(maskx, masky, roicols, roirows)); - gmat_whole = mat; - gmat = gmat_whole(Rect(srcx, srcy, roicols, roirows)); + gsrc_whole = src; + gsrc = gsrc_whole(Rect(srcx, srcy, roicols, roirows)); gmask = mask_roi; } @@ -341,10 +330,10 @@ TEST_P(SetTo, Without_mask) { random_roi(); - mat_roi.setTo(val); - gmat.setTo(val); + src_roi.setTo(val); + gsrc.setTo(val); - EXPECT_MAT_NEAR(mat, Mat(gmat_whole), 1.); + EXPECT_MAT_NEAR(src, Mat(gsrc_whole), 1.); } } @@ -354,10 +343,10 @@ TEST_P(SetTo, With_mask) { random_roi(); - mat_roi.setTo(val, mask_roi); - gmat.setTo(val, gmask); + src_roi.setTo(val, mask_roi); + gsrc.setTo(val, gmask); - EXPECT_MAT_NEAR(mat, Mat(gmat_whole), 1.); + EXPECT_MAT_NEAR(src, Mat(gsrc_whole), 1.); } } @@ -431,12 +420,12 @@ INSTANTIATE_TEST_CASE_P(MatrixOperation, ConvertTo, Combine( Range(1, 5), Bool())); INSTANTIATE_TEST_CASE_P(MatrixOperation, CopyTo, Combine( - Values(CV_8UC1, CV_8UC3, CV_8UC4, CV_32SC1, CV_32SC3, CV_32SC4, CV_32FC1, CV_32FC3, CV_32FC4), - Bool())); + Values(CV_8U, CV_8S, CV_16U, CV_16S, CV_32S, CV_32F, CV_64F), + testing::Range(1, 5), Bool())); INSTANTIATE_TEST_CASE_P(MatrixOperation, SetTo, Combine( - Values(CV_8UC1, CV_8UC3, CV_8UC4, CV_32SC1, CV_32SC3, CV_32SC4, CV_32FC1, CV_32FC3, CV_32FC4), - Bool())); + Values(CV_8U, CV_8S, CV_16U, CV_16S, CV_32S, CV_32F, CV_64F), + testing::Range(1, 5), Bool())); INSTANTIATE_TEST_CASE_P(MatrixOperation, convertC3C4, Combine( Values(CV_8U, CV_8S, CV_16U, CV_16S, CV_32S, CV_32F, CV_64F), From 6fb3b4f41c6ff3a9b173d0d2b1433aab0751f6b7 Mon Sep 17 00:00:00 2001 From: Csaba Kertesz Date: Wed, 18 Sep 2013 21:49:32 +0200 Subject: [PATCH 31/68] Fix a memory leak in CvNormalBayesClassifier::train() --- modules/ml/src/nbayes.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/ml/src/nbayes.cpp b/modules/ml/src/nbayes.cpp index b9a966cd1d..5ad1b134d9 100644 --- a/modules/ml/src/nbayes.cpp +++ b/modules/ml/src/nbayes.cpp @@ -210,6 +210,8 @@ bool CvNormalBayesClassifier::train( const CvMat* _train_data, const CvMat* _res prod_data[c2] += train_vec[c2]*val1; } } + cvReleaseMat( &responses ); + responses = 0; /* calculate avg, covariance matrix, c */ for( cls = 0; cls < nclasses; cls++ ) From 286244efedd215600b6be72d4c25e48af0ed1c2d Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Sat, 21 Sep 2013 21:12:39 +0400 Subject: [PATCH 32/68] cmake: fixed incorrect usage of add_definitions() on Linux --- cmake/OpenCVModule.cmake | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/cmake/OpenCVModule.cmake b/cmake/OpenCVModule.cmake index 4076d9fab7..1d87bc1b88 100644 --- a/cmake/OpenCVModule.cmake +++ b/cmake/OpenCVModule.cmake @@ -519,11 +519,7 @@ macro(ocv_create_module) if((NOT DEFINED OPENCV_MODULE_TYPE AND BUILD_SHARED_LIBS) OR (DEFINED OPENCV_MODULE_TYPE AND OPENCV_MODULE_TYPE STREQUAL SHARED)) - if(MSVC) - set_target_properties(${the_module} PROPERTIES DEFINE_SYMBOL CVAPI_EXPORTS) - else() - add_definitions(-DCVAPI_EXPORTS) - endif() + set_target_properties(${the_module} PROPERTIES DEFINE_SYMBOL CVAPI_EXPORTS) endif() if(MSVC) From 06c33df307c25a5aac830e697ce57bc4858c4060 Mon Sep 17 00:00:00 2001 From: Jin Ma Date: Sun, 22 Sep 2013 10:22:09 +0800 Subject: [PATCH 33/68] Added knearest neighbor of OpenCL version. It includes the accuracy/performance test and the implementation of KNN. --- modules/ocl/CMakeLists.txt | 2 +- modules/ocl/include/opencv2/ocl/ocl.hpp | 24 +++++++++++++++++++ .../ocl/include/opencv2/ocl/private/util.hpp | 1 + modules/ocl/src/initialization.cpp | 8 +++++++ 4 files changed, 34 insertions(+), 1 deletion(-) diff --git a/modules/ocl/CMakeLists.txt b/modules/ocl/CMakeLists.txt index 05b28b83fe..69d9df52d8 100644 --- a/modules/ocl/CMakeLists.txt +++ b/modules/ocl/CMakeLists.txt @@ -3,5 +3,5 @@ if(NOT HAVE_OPENCL) endif() set(the_description "OpenCL-accelerated Computer Vision") -ocv_define_module(ocl opencv_core opencv_imgproc opencv_features2d opencv_objdetect opencv_video opencv_calib3d) +ocv_define_module(ocl opencv_core opencv_imgproc opencv_features2d opencv_objdetect opencv_video opencv_calib3d opencv_ml) ocv_warnings_disable(CMAKE_CXX_FLAGS -Wshadow) diff --git a/modules/ocl/include/opencv2/ocl/ocl.hpp b/modules/ocl/include/opencv2/ocl/ocl.hpp index 42ac758408..8f46244fae 100644 --- a/modules/ocl/include/opencv2/ocl/ocl.hpp +++ b/modules/ocl/include/opencv2/ocl/ocl.hpp @@ -51,6 +51,7 @@ #include "opencv2/imgproc/imgproc.hpp" #include "opencv2/objdetect/objdetect.hpp" #include "opencv2/features2d/features2d.hpp" +#include "opencv2/ml/ml.hpp" namespace cv { @@ -1892,6 +1893,29 @@ namespace cv { return (total + grain - 1) / grain; } + + /*!***************K Nearest Neighbour*************!*/ + class CV_EXPORTS KNearestNeighbour: public CvKNearest + { + public: + KNearestNeighbour(); + ~KNearestNeighbour(); + KNearestNeighbour(const Mat& trainData, const Mat& labels, + const Mat& sampleIdx = Mat().setTo(Scalar::all(0)), bool isRegression = false, int max_k = 32); + + bool train(const Mat& trainData, Mat& labels, Mat& sampleIdx = Mat().setTo(Scalar::all(0)), + bool isRegression = false, int max_k = 32, bool updateBase = false); + + void clear(); + + void find_nearest(const oclMat& samples, int k, oclMat& lables); + + private: + int max_k, var_count; + int total; + bool regression; + oclMat samples_ocl; + }; } } #if defined _MSC_VER && _MSC_VER >= 1200 diff --git a/modules/ocl/include/opencv2/ocl/private/util.hpp b/modules/ocl/include/opencv2/ocl/private/util.hpp index 3176a68951..9adae38230 100644 --- a/modules/ocl/include/opencv2/ocl/private/util.hpp +++ b/modules/ocl/include/opencv2/ocl/private/util.hpp @@ -167,6 +167,7 @@ namespace cv template<> bool CV_EXPORTS queryDeviceInfo(cl_kernel kernel); + unsigned long CV_EXPORTS queryLocalMemInfo(); }//namespace ocl }//namespace cv diff --git a/modules/ocl/src/initialization.cpp b/modules/ocl/src/initialization.cpp index 0e16e75aeb..34d5ff5e6f 100644 --- a/modules/ocl/src/initialization.cpp +++ b/modules/ocl/src/initialization.cpp @@ -1033,6 +1033,14 @@ namespace cv return impl->maxComputeUnits; } + unsigned long queryLocalMemInfo() + { + Info::Impl* impl = Context::getContext()->impl; + cl_ulong local_memory_size = 0; + clGetDeviceInfo(impl->devices[impl->devnum], CL_DEVICE_LOCAL_MEM_SIZE, sizeof(cl_ulong), (void*)&local_memory_size, 0); + return local_memory_size; + } + void* Context::oclContext() { return impl->oclcontext; From 1bfe39f485ba0b5e00c2d9d64a2e3ed61e1f67d4 Mon Sep 17 00:00:00 2001 From: Jin Ma Date: Sun, 22 Sep 2013 10:23:54 +0800 Subject: [PATCH 34/68] Added knearest neighbor of OpenCL version. It includes the accuracy/performance test and the implementation of KNN. --- modules/ocl/perf/perf_ml.cpp | 109 +++++++++++++++++ modules/ocl/src/knearest.cpp | 163 +++++++++++++++++++++++++ modules/ocl/src/opencl/knearest.cl | 186 +++++++++++++++++++++++++++++ modules/ocl/test/test_ml.cpp | 124 +++++++++++++++++++ 4 files changed, 582 insertions(+) create mode 100644 modules/ocl/perf/perf_ml.cpp create mode 100644 modules/ocl/src/knearest.cpp create mode 100644 modules/ocl/src/opencl/knearest.cl create mode 100644 modules/ocl/test/test_ml.cpp diff --git a/modules/ocl/perf/perf_ml.cpp b/modules/ocl/perf/perf_ml.cpp new file mode 100644 index 0000000000..fac471ed4c --- /dev/null +++ b/modules/ocl/perf/perf_ml.cpp @@ -0,0 +1,109 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2010-2012, Multicoreware, Inc., all rights reserved. +// Copyright (C) 2010-2012, Advanced Micro Devices, Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// @Authors +// Jin Ma, jin@multicorewareinc.com +// Xiaopeng Fu, fuxiaopeng2222@163.com +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other oclMaterials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors as is and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ +#include "perf_precomp.hpp" +using namespace perf; +using namespace std; +using namespace cv::ocl; +using namespace cv; +using std::tr1::tuple; +using std::tr1::get; +////////////////////////////////// K-NEAREST NEIGHBOR //////////////////////////////////// +static void genData(Mat& trainData, Size size, Mat& trainLabel = Mat().setTo(Scalar::all(0)), int nClasses = 0) +{ + trainData.create(size, CV_32FC1); + randu(trainData, 1.0, 100.0); + + if(nClasses != 0) + { + trainLabel.create(size.height, 1, CV_8UC1); + randu(trainLabel, 0, nClasses - 1); + trainLabel.convertTo(trainLabel, CV_32FC1); + } +} + +typedef tuple KNNParamType; +typedef TestBaseWithParam KNNFixture; + +PERF_TEST_P(KNNFixture, KNN, + testing::Values(1000, 2000, 4000)) +{ + KNNParamType params = GetParam(); + const int rows = get<0>(params); + int columns = 100; + int k = rows/250; + + Mat trainData, trainLabels; + Size size(columns, rows); + genData(trainData, size, trainLabels, 3); + + Mat testData; + genData(testData, size); + Mat best_label; + + if(RUN_PLAIN_IMPL) + { + TEST_CYCLE() + { + CvKNearest knn_cpu; + knn_cpu.train(trainData, trainLabels); + knn_cpu.find_nearest(testData, k, &best_label); + } + }else if(RUN_OCL_IMPL) + { + cv::ocl::oclMat best_label_ocl; + cv::ocl::oclMat testdata; + testdata.upload(testData); + + OCL_TEST_CYCLE() + { + cv::ocl::KNearestNeighbour knn_ocl; + knn_ocl.train(trainData, trainLabels); + knn_ocl.find_nearest(testdata, k, best_label_ocl); + } + best_label_ocl.download(best_label); + }else + OCL_PERF_ELSE + SANITY_CHECK(best_label); +} \ No newline at end of file diff --git a/modules/ocl/src/knearest.cpp b/modules/ocl/src/knearest.cpp new file mode 100644 index 0000000000..4f78e85ea8 --- /dev/null +++ b/modules/ocl/src/knearest.cpp @@ -0,0 +1,163 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2010-2012, Advanced Micro Devices, Inc., all rights reserved. +// Copyright (C) 2010-2012, Multicoreware, Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// @Authors +// Jin Ma, jin@multicorewareinc.com +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other oclMaterials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#include "precomp.hpp" +using namespace cv; +using namespace cv::ocl; + +namespace cv +{ + namespace ocl + { + extern const char* knearest;//knearest + } +} + +KNearestNeighbour::KNearestNeighbour() +{ + clear(); +} + +KNearestNeighbour::~KNearestNeighbour() +{ + clear(); +} + +KNearestNeighbour::KNearestNeighbour(const Mat& train_data, const Mat& responses, + const Mat& sample_idx, bool is_regression, int max_k) +{ + max_k = max_k; + CvKNearest::train(train_data, responses, sample_idx, is_regression, max_k); +} + +void KNearestNeighbour::clear() +{ + CvKNearest::clear(); +} + +bool KNearestNeighbour::train(const Mat& trainData, Mat& labels, Mat& sampleIdx, + bool isRegression, int _max_k, bool updateBase) +{ + max_k = _max_k; + bool cv_knn_train = CvKNearest::train(trainData, labels, sampleIdx, isRegression, max_k, updateBase); + + CvVectors* s = CvKNearest::samples; + + cv::Mat samples_mat(s->count, CvKNearest::var_count + 1, s->type); + + float* s1 = (float*)(s + 1); + for(int i = 0; i < s->count; i++) + { + float* t1 = s->data.fl[i]; + for(int j = 0; j < CvKNearest::var_count; j++) + { + Point pos(j, i); + samples_mat.at(pos) = t1[j]; + } + + Point pos_label(CvKNearest::var_count, i); + samples_mat.at(pos_label) = s1[i]; + } + + samples_ocl = samples_mat; + return cv_knn_train; +} + +void KNearestNeighbour::find_nearest(const oclMat& samples, int k, oclMat& lables) +{ + CV_Assert(!samples_ocl.empty()); + lables.create(samples.rows, 1, CV_32FC1); + + CV_Assert(samples.cols == CvKNearest::var_count); + CV_Assert(samples.type() == CV_32FC1); + CV_Assert(k >= 1 && k <= max_k); + + int k1 = KNearest::get_sample_count(); + k1 = MIN( k1, k ); + + String kernel_name = "knn_find_nearest"; + cl_ulong local_memory_size = queryLocalMemInfo(); + int nThreads = local_memory_size / (2 * k * 4); + if(nThreads >= 256) + nThreads = 256; + + int smem_size = nThreads * k * 4 * 2; + size_t local_thread[] = {1, nThreads, 1}; + size_t global_thread[] = {1, samples.rows, 1}; + + char build_option[50]; + if(!Context::getContext()->supportsFeature(Context::CL_DOUBLE)) + { + sprintf(build_option, " "); + }else + sprintf(build_option, "-D DOUBLE_SUPPORT"); + + std::vector< std::pair > args; + + int samples_ocl_step = samples_ocl.step/samples_ocl.elemSize(); + int samples_step = samples.step/samples.elemSize(); + int lables_step = lables.step/lables.elemSize(); + + int _regression = 0; + if(CvKNearest::regression) + _regression = 1; + + args.push_back(make_pair(sizeof(cl_mem), (void*)&samples.data)); + args.push_back(make_pair(sizeof(cl_int), (void*)&samples.rows)); + args.push_back(make_pair(sizeof(cl_int), (void*)&samples.cols)); + args.push_back(make_pair(sizeof(cl_int), (void*)&samples_step)); + args.push_back(make_pair(sizeof(cl_int), (void*)&k)); + args.push_back(make_pair(sizeof(cl_mem), (void*)&samples_ocl.data)); + args.push_back(make_pair(sizeof(cl_int), (void*)&samples_ocl.rows)); + args.push_back(make_pair(sizeof(cl_int), (void*)&samples_ocl_step)); + args.push_back(make_pair(sizeof(cl_mem), (void*)&lables.data)); + args.push_back(make_pair(sizeof(cl_int), (void*)&lables_step)); + args.push_back(make_pair(sizeof(cl_int), (void*)&_regression)); + args.push_back(make_pair(sizeof(cl_int), (void*)&k1)); + args.push_back(make_pair(sizeof(cl_int), (void*)&samples_ocl.cols)); + args.push_back(make_pair(sizeof(cl_int), (void*)&nThreads)); + args.push_back(make_pair(smem_size, (void*)NULL)); + openCLExecuteKernel(Context::getContext(), &knearest, kernel_name, global_thread, local_thread, args, -1, -1, build_option); +} \ No newline at end of file diff --git a/modules/ocl/src/opencl/knearest.cl b/modules/ocl/src/opencl/knearest.cl new file mode 100644 index 0000000000..47af57a7e2 --- /dev/null +++ b/modules/ocl/src/opencl/knearest.cl @@ -0,0 +1,186 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2010-2012, Multicoreware, Inc., all rights reserved. +// Copyright (C) 2010-2012, Advanced Micro Devices, Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// @Authors +// Jin Ma, jin@multicorewareinc.com +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other oclMaterials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors as is and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ +#if defined (DOUBLE_SUPPORT) +#pragma OPENCL EXTENSION cl_khr_fp64:enable +#define TYPE double +#else +#define TYPE float +#endif + +#define CV_SWAP(a,b,t) ((t) = (a), (a) = (b), (b) = (t)) +///////////////////////////////////// find_nearest ////////////////////////////////////// +__kernel void knn_find_nearest(__global float* sample, int sample_row, int sample_col, int sample_step, + int k, __global float* samples_ocl, int sample_ocl_row, int sample_ocl_step, + __global float* _results, int _results_step, int _regression, int K1, + int sample_ocl_col, int nThreads, __local float* nr) +{ + int k1 = 0; + int k2 = 0; + + bool regression = false; + + if(_regression) + regression = true; + + TYPE inv_scale; +#ifdef DOUBLE_SUPPORT + inv_scale = 1.0/K1; +#else + inv_scale = 1.0f/K1; +#endif + + int y = get_global_id(1); + int j, j1; + int threadY = (y % nThreads); + __local float* dd = nr + nThreads * k; + if(y >= sample_row) + { + return; + } + for(j = 0; j < sample_ocl_row; j++) + { + TYPE sum; +#ifdef DOUBLE_SUPPORT + sum = 0.0; +#else + sum = 0.0f; +#endif + float si; + int t, ii, ii1; + for(t = 0; t < sample_col - 16; t += 16) + { + float16 t0 = vload16(0, sample + y * sample_step + t) - vload16(0, samples_ocl + j * sample_ocl_step + t); + t0 *= t0; + sum += t0.s0 + t0.s1 + t0.s2 + t0.s3 + t0.s4 + t0.s5 + t0.s6 + t0.s7 + + t0.s8 + t0.s9 + t0.sa + t0.sb + t0.sc + t0.sd + t0.se + t0.sf; + } + + for(; t < sample_col; t++) + { +#ifdef DOUBLE_SUPPORT + double t0 = sample[y * sample_step + t] - samples_ocl[j * sample_ocl_step + t]; +#else + float t0 = sample[y * sample_step + t] - samples_ocl[j * sample_ocl_step + t]; +#endif + sum = sum + t0 * t0; + } + + si = (float)sum; + for(ii = k1 - 1; ii >= 0; ii--) + { + if(as_int(si) > as_int(dd[ii * nThreads + threadY])) + break; + } + if(ii < k - 1) + { + for(ii1 = k2 - 1; ii1 > ii; ii1--) + { + dd[(ii1 + 1) * nThreads + threadY] = dd[ii1 * nThreads + threadY]; + nr[(ii1 + 1) * nThreads + threadY] = nr[ii1 * nThreads + threadY]; + } + + dd[(ii + 1) * nThreads + threadY] = si; + nr[(ii + 1) * nThreads + threadY] = samples_ocl[sample_col + j * sample_ocl_step]; + } + k1 = (k1 + 1) < k ? (k1 + 1) : k; + k2 = k1 < (k - 1) ? k1 : (k - 1); + } + /*! find_nearest_neighbor done!*/ + /*! write_results start!*/ + switch (regression) + { + case true: + { + TYPE s; +#ifdef DOUBLE_SUPPORT + s = 0.0; +#else + s = 0.0f; +#endif + for(j = 0; j < K1; j++) + s += nr[j * nThreads + threadY]; + + _results[y * _results_step] = (float)(s * inv_scale); + } + break; + case false: + { + int prev_start = 0, best_count = 0, cur_count; + float best_val; + + for(j = K1 - 1; j > 0; j--) + { + bool swap_f1 = false; + for(j1 = 0; j1 < j; j1++) + { + if(nr[j1 * nThreads + threadY] > nr[(j1 + 1) * nThreads + threadY]) + { + int t; + CV_SWAP(nr[j1 * nThreads + threadY], nr[(j1 + 1) * nThreads + threadY], t); + swap_f1 = true; + } + } + if(!swap_f1) + break; + } + + best_val = 0; + for(j = 1; j <= K1; j++) + if(j == K1 || nr[j * nThreads + threadY] != nr[(j - 1) * nThreads + threadY]) + { + cur_count = j - prev_start; + if(best_count < cur_count) + { + best_count = cur_count; + best_val = nr[(j - 1) * nThreads + threadY]; + } + prev_start = j; + } + _results[y * _results_step] = best_val; + } + break; + } + ///*! write_results done!*/ +} diff --git a/modules/ocl/test/test_ml.cpp b/modules/ocl/test/test_ml.cpp new file mode 100644 index 0000000000..834fc4e377 --- /dev/null +++ b/modules/ocl/test/test_ml.cpp @@ -0,0 +1,124 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// Copyright (C) 2010-2012, Multicoreware, Inc., all rights reserved. +// Copyright (C) 2010-2012, Advanced Micro Devices, Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// @Authors +// Jin Ma, jin@multicorewareinc.com +// Xiaopeng Fu, fuxiaopeng2222@163.com +// Erping Pang, pang_er_ping@163.com +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other oclMaterials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#include "test_precomp.hpp" +#ifdef HAVE_OPENCL +using namespace cv; +using namespace cv::ocl; +using namespace cvtest; +using namespace testing; +///////K-NEAREST NEIGHBOR////////////////////////// +static void genTrainData(Mat& trainData, int trainDataRow, int trainDataCol, + Mat& trainLabel = Mat().setTo(Scalar::all(0)), int nClasses = 0) +{ + cv::RNG &rng = TS::ptr()->get_rng(); + cv::Size size(trainDataCol, trainDataRow); + trainData = randomMat(rng, size, CV_32FC1, 1.0, 1000.0, false); + if(nClasses != 0) + { + cv::Size size1(trainDataRow, 1); + trainLabel = randomMat(rng, size1, CV_8UC1, 0, nClasses - 1, false); + trainLabel.convertTo(trainLabel, CV_32FC1); + } +} + +PARAM_TEST_CASE(KNN, int, Size, int, bool) +{ + int k; + int trainDataCol; + int testDataRow; + int nClass; + bool regression; + virtual void SetUp() + { + k = GET_PARAM(0); + nClass = GET_PARAM(2); + trainDataCol = GET_PARAM(1).width; + testDataRow = GET_PARAM(1).height; + regression = GET_PARAM(3); + } +}; + +TEST_P(KNN, Accuracy) +{ + Mat trainData, trainLabels; + const int trainDataRow = 500; + genTrainData(trainData, trainDataRow, trainDataCol, trainLabels, nClass); + + Mat testData, testLabels; + genTrainData(testData, testDataRow, trainDataCol); + + KNearestNeighbour knn_ocl; + CvKNearest knn_cpu; + Mat best_label_cpu; + oclMat best_label_ocl; + + /*ocl k-Nearest_Neighbor start*/ + oclMat trainData_ocl; + trainData_ocl.upload(trainData); + Mat simpleIdx; + knn_ocl.train(trainData, trainLabels, simpleIdx, regression); + + oclMat testdata; + testdata.upload(testData); + knn_ocl.find_nearest(testdata, k, best_label_ocl); + /*ocl k-Nearest_Neighbor end*/ + + /*cpu k-Nearest_Neighbor start*/ + knn_cpu.train(trainData, trainLabels, simpleIdx, regression); + knn_cpu.find_nearest(testData, k, &best_label_cpu); + /*cpu k-Nearest_Neighbor end*/ + if(regression) + { + EXPECT_MAT_SIMILAR(Mat(best_label_ocl), best_label_cpu, 1e-5); + } + else + { + EXPECT_MAT_NEAR(Mat(best_label_ocl), best_label_cpu, 0.0); + } +} +INSTANTIATE_TEST_CASE_P(OCL_ML, KNN, Combine(Values(6, 5), Values(Size(200, 400), Size(300, 600)), + Values(4, 3), Values(false, true))); +#endif // HAVE_OPENCL \ No newline at end of file From b88f5302e20da8d477eee6e9d361d1f3983784ca Mon Sep 17 00:00:00 2001 From: Jin Ma Date: Mon, 23 Sep 2013 15:10:00 +0800 Subject: [PATCH 35/68] Removed unused constructor. --- modules/ocl/include/opencv2/ocl/ocl.hpp | 5 ----- modules/ocl/src/knearest.cpp | 8 +------- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/modules/ocl/include/opencv2/ocl/ocl.hpp b/modules/ocl/include/opencv2/ocl/ocl.hpp index 8f46244fae..dc9183acba 100644 --- a/modules/ocl/include/opencv2/ocl/ocl.hpp +++ b/modules/ocl/include/opencv2/ocl/ocl.hpp @@ -1900,8 +1900,6 @@ namespace cv public: KNearestNeighbour(); ~KNearestNeighbour(); - KNearestNeighbour(const Mat& trainData, const Mat& labels, - const Mat& sampleIdx = Mat().setTo(Scalar::all(0)), bool isRegression = false, int max_k = 32); bool train(const Mat& trainData, Mat& labels, Mat& sampleIdx = Mat().setTo(Scalar::all(0)), bool isRegression = false, int max_k = 32, bool updateBase = false); @@ -1911,9 +1909,6 @@ namespace cv void find_nearest(const oclMat& samples, int k, oclMat& lables); private: - int max_k, var_count; - int total; - bool regression; oclMat samples_ocl; }; } diff --git a/modules/ocl/src/knearest.cpp b/modules/ocl/src/knearest.cpp index 4f78e85ea8..fd9f2fed57 100644 --- a/modules/ocl/src/knearest.cpp +++ b/modules/ocl/src/knearest.cpp @@ -63,13 +63,7 @@ KNearestNeighbour::KNearestNeighbour() KNearestNeighbour::~KNearestNeighbour() { clear(); -} - -KNearestNeighbour::KNearestNeighbour(const Mat& train_data, const Mat& responses, - const Mat& sample_idx, bool is_regression, int max_k) -{ - max_k = max_k; - CvKNearest::train(train_data, responses, sample_idx, is_regression, max_k); + samples_ocl.release(); } void KNearestNeighbour::clear() From 1b1fb131b353c249c75a402d9ce7b28d2d5b3422 Mon Sep 17 00:00:00 2001 From: StevenPuttemans Date: Tue, 17 Sep 2013 13:05:28 +0200 Subject: [PATCH 36/68] Attempt at fixing bug 3186 --- modules/highgui/src/cap_v4l.cpp | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/modules/highgui/src/cap_v4l.cpp b/modules/highgui/src/cap_v4l.cpp index 045c6f889c..cfb115328a 100644 --- a/modules/highgui/src/cap_v4l.cpp +++ b/modules/highgui/src/cap_v4l.cpp @@ -248,6 +248,10 @@ make & enjoy! #define CHANNEL_NUMBER 1 #define MAX_CAMERAS 8 +// Extra global values declared for error capture +#define CAPV4L2_CAMERA_UNPLUGGED 2 +#define CAPV4L2_OK 1 +#define CAPV4L2_FAIL 0 // default and maximum number of V4L buffers, not including last, 'special' buffer #define MAX_V4L_BUFFERS 10 @@ -1236,10 +1240,10 @@ static int read_frame_v4l2(CvCaptureCAM_V4L* capture) { //set timestamp in capture struct to be timestamp of most recent frame capture->timestamp = buf.timestamp; - return 1; + return CAPV4L2_CAMERA_UNPLUGGED; } -static void mainloop_v4l2(CvCaptureCAM_V4L* capture) { +static int mainloop_v4l2(CvCaptureCAM_V4L* capture) { unsigned int count; count = 1; @@ -1273,8 +1277,11 @@ static void mainloop_v4l2(CvCaptureCAM_V4L* capture) { break; } - if (read_frame_v4l2 (capture)) - break; + switch(int readresult = read_frame_v4l2(capture) ) + case CAM_UNPLUGGED: + return CAPV4L2_FAIL; + default: + return CAPV4L2_OK; } } } @@ -1354,7 +1361,10 @@ static int icvGrabFrameCAM_V4L(CvCaptureCAM_V4L* capture) { { // skip first frame. it is often bad -- this is unnotied in traditional apps, // but could be fatal if bad jpeg is enabled - mainloop_v4l2(capture); + if(!mainloop_v4l2(capture)){ + fprintf( stderr, "HIGHGUI ERROR: V4L: Could not capture image.\n"); + return 0; + } } #endif @@ -1367,7 +1377,10 @@ static int icvGrabFrameCAM_V4L(CvCaptureCAM_V4L* capture) { if (V4L2_SUPPORT == 1) { - mainloop_v4l2(capture); + if(!mainloop_v4l2(capture)){ + fprintf( stderr, "HIGHGUI ERROR: V4L: Could not capture image.\n"); + return 0; + } } #endif /* HAVE_CAMV4L2 */ From d64fb235f75226c0df6d9bd5b812ce7abe4a63a7 Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Tue, 24 Sep 2013 13:15:05 +0400 Subject: [PATCH 37/68] removed needless arithm_2_mat.cl file --- modules/ocl/src/opencl/arithm_2_mat.cl | 158 ------------------------- 1 file changed, 158 deletions(-) delete mode 100644 modules/ocl/src/opencl/arithm_2_mat.cl diff --git a/modules/ocl/src/opencl/arithm_2_mat.cl b/modules/ocl/src/opencl/arithm_2_mat.cl deleted file mode 100644 index 63c1ccac03..0000000000 --- a/modules/ocl/src/opencl/arithm_2_mat.cl +++ /dev/null @@ -1,158 +0,0 @@ -/*M/////////////////////////////////////////////////////////////////////////////////////// -// -// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. -// -// By downloading, copying, installing or using the software you agree to this license. -// If you do not agree to this license, do not download, install, -// copy or use the software. -// -// -// License Agreement -// For Open Source Computer Vision Library -// -// Copyright (C) 2010-2012, Institute Of Software Chinese Academy Of Science, all rights reserved. -// Copyright (C) 2010-2012, Advanced Micro Devices, Inc., all rights reserved. -// Third party copyrights are property of their respective owners. -// -// @Authors -// Shengen Yan,yanshengen@gmail.com -// -// Redistribution and use in source and binary forms, with or without modification, -// are permitted provided that the following conditions are met: -// -// * Redistribution's of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// * Redistribution's in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other oclMaterials provided with the distribution. -// -// * The name of the copyright holders may not be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// This software is provided by the copyright holders and contributors as is and -// any express or implied warranties, including, but not limited to, the implied -// warranties of merchantability and fitness for a particular purpose are disclaimed. -// In no event shall the Intel Corporation or contributors be liable for any direct, -// indirect, incidental, special, exemplary, or consequential damages -// (including, but not limited to, procurement of substitute goods or services; -// loss of use, data, or profits; or business interruption) however caused -// and on any theory of liability, whether in contract, strict liability, -// or tort (including negligence or otherwise) arising in any way out of -// the use of this software, even if advised of the possibility of such damage. -// -//M*/ - -/**************************************PUBLICFUNC*************************************/ -#if defined (DOUBLE_SUPPORT) -#pragma OPENCL EXTENSION cl_khr_fp64:enable -#endif - -#pragma OPENCL EXTENSION cl_khr_global_int32_base_atomics:enable -#pragma OPENCL EXTENSION cl_khr_global_int32_extended_atomics:enable -#define CV_PI 3.1415926535897932384626433832795 - -char round_char(double v){ - char v1=(char)v; - return convert_char_sat(v+(v>=0 ? 0.5 : -0.5)); -} -unsigned char round_uchar(double v){ - unsigned char v1=(unsigned char)v; - return convert_uchar_sat(v+(v>=0 ? 0.5 : -0.5)); -} -short round_short(double v){ - short v1=(short)v; - return convert_short_sat(v+(v>=0 ? 0.5 : -0.5)); -} -unsigned short round_ushort(double v){ - unsigned short v1=(unsigned short)v; - return convert_ushort_sat(v+(v>=0 ? 0.5 : -0.5)); -} -int round_int(double v){ - int v1=(int)v; - return convert_int_sat(v+(v>=0 ? 0.5 : -0.5)); -} - -char round2_char(double v){ - char v1=(char)v; - if((v-v1)==0.5&&v1%2==0) - return v1; - else - return convert_char_sat(v+(v>=0 ? 0.5 : -0.5)); -} -unsigned char round2_uchar(double v){ - unsigned char v1=(unsigned char)v; - if((v-v1)==0.5&&v1%2==0) - return v1; - else - return convert_uchar_sat(v+(v>=0 ? 0.5 : -0.5)); -} -short round2_short(double v){ - short v1=(short)v; - if((v-v1)==0.5&&v1%2==0) - return v1; - else - return convert_short_sat(v+(v>=0 ? 0.5 : -0.5)); -} -unsigned short round2_ushort(double v){ - unsigned short v1=(unsigned short)v; - if((v-v1)==0.5&&v1%2==0) - return v1; - else - return convert_ushort_sat(v+(v>=0 ? 0.5 : -0.5)); -} -int round2_int(double v){ - int v1=(int)v; - if((v-v1)==0.5&&v1%2==0) - return v1; - else - return convert_int_sat(v+(v>=0 ? 0.5 : -0.5)); -} - -/*****************************************EXP***************************************/ -__kernel void arithm_op_exp_5 (int rows,int cols,int srcStep,__global float *src1Mat, - __global float * dstMat,int channels) -{ - size_t x = get_global_id(0); - size_t y = get_global_id(1); - if (x < cols && y < rows) - { - size_t idx = y * ( srcStep >> 2 ) + x; - dstMat[idx] = (float)exp((float)src1Mat[idx]); - } -} -__kernel void arithm_op_exp_6 (int rows,int cols,int srcStep,__global double *src1Mat, - __global double * dstMat,int channels) -{ - size_t x = get_global_id(0); - size_t y = get_global_id(1); - if (x < cols && y < rows) - { - size_t idx = y * ( srcStep >> 3 ) + x; - dstMat[idx] = exp(src1Mat[idx]); - } -} - -/*****************************************LOG***************************************/ -__kernel void arithm_op_log_5 (int rows,int cols,int srcStep,__global float *src1Mat, - __global float * dstMat,int channels) -{ - size_t x = get_global_id(0); - size_t y = get_global_id(1); - if (x < cols && y < rows) - { - size_t idx = y * ( srcStep >> 2 ) + x; - dstMat[idx] =(float) log((float)src1Mat[idx]); - } -} -__kernel void arithm_op_log_6 (int rows,int cols,int srcStep,__global double *src1Mat, - __global double * dstMat,int channels) -{ - size_t x = get_global_id(0); - size_t y = get_global_id(1); - if (x < cols && y < rows) - { - size_t idx = y * ( srcStep >> 3 ) + x; - dstMat[idx] = log(src1Mat[idx]); - } -} From 5ff5fdd73de147c014c4780e2aa90ea20209ed6f Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Tue, 24 Sep 2013 13:17:09 +0400 Subject: [PATCH 38/68] marked some methods of ocl::Context as const --- modules/ocl/include/opencv2/ocl/ocl.hpp | 4 ++-- modules/ocl/src/initialization.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/ocl/include/opencv2/ocl/ocl.hpp b/modules/ocl/include/opencv2/ocl/ocl.hpp index dc9183acba..361e29251e 100644 --- a/modules/ocl/include/opencv2/ocl/ocl.hpp +++ b/modules/ocl/include/opencv2/ocl/ocl.hpp @@ -158,8 +158,8 @@ namespace cv static void setContext(Info &oclinfo); enum {CL_DOUBLE, CL_UNIFIED_MEM, CL_VER_1_2}; - bool supportsFeature(int ftype); - size_t computeUnits(); + bool supportsFeature(int ftype) const; + size_t computeUnits() const; void* oclContext(); void* oclCommandQueue(); }; diff --git a/modules/ocl/src/initialization.cpp b/modules/ocl/src/initialization.cpp index 34d5ff5e6f..c18984b078 100644 --- a/modules/ocl/src/initialization.cpp +++ b/modules/ocl/src/initialization.cpp @@ -1013,7 +1013,7 @@ namespace cv programCache->releaseProgram(); } - bool Context::supportsFeature(int ftype) + bool Context::supportsFeature(int ftype) const { switch(ftype) { @@ -1028,7 +1028,7 @@ namespace cv } } - size_t Context::computeUnits() + size_t Context::computeUnits() const { return impl->maxComputeUnits; } From 0ad03162dff9dc500fb1d80cdf33e7c9e00cc961 Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Tue, 24 Sep 2013 13:34:55 +0400 Subject: [PATCH 39/68] refactored and extended arithm operations add/sub/mul/div/absdiff --- modules/ocl/include/opencv2/ocl/ocl.hpp | 19 +- modules/ocl/src/arithm.cpp | 427 +++------- modules/ocl/src/opencl/arithm_add.cl | 806 ++---------------- modules/ocl/src/opencl/arithm_add_mask.cl | 79 ++ modules/ocl/src/opencl/arithm_add_scalar.cl | 458 +--------- .../ocl/src/opencl/arithm_add_scalar_mask.cl | 563 +----------- modules/ocl/src/opencl/arithm_div.cl | 468 ---------- modules/ocl/src/opencl/arithm_mul.cl | 303 ------- 8 files changed, 287 insertions(+), 2836 deletions(-) create mode 100644 modules/ocl/src/opencl/arithm_add_mask.cl delete mode 100644 modules/ocl/src/opencl/arithm_div.cl delete mode 100644 modules/ocl/src/opencl/arithm_mul.cl diff --git a/modules/ocl/include/opencv2/ocl/ocl.hpp b/modules/ocl/include/opencv2/ocl/ocl.hpp index 361e29251e..2bfc7db454 100644 --- a/modules/ocl/include/opencv2/ocl/ocl.hpp +++ b/modules/ocl/include/opencv2/ocl/ocl.hpp @@ -409,40 +409,37 @@ namespace cv CV_EXPORTS void split(const oclMat &src, vector &dst); ////////////////////////////// Arithmetics /////////////////////////////////// + //#if defined DOUBLE_SUPPORT //typedef double F; //#else //typedef float F; //#endif + // CV_EXPORTS void addWeighted(const oclMat& a,F alpha, const oclMat& b,F beta,F gama, oclMat& c); CV_EXPORTS void addWeighted(const oclMat &a, double alpha, const oclMat &b, double beta, double gama, oclMat &c); + //! adds one matrix to another (c = a + b) // supports all types except CV_8SC1,CV_8SC2,CV8SC3 and CV_8SC4 - CV_EXPORTS void add(const oclMat &a, const oclMat &b, oclMat &c); - //! adds one matrix to another (c = a + b) - // supports all types except CV_8SC1,CV_8SC2,CV8SC3 and CV_8SC4 - CV_EXPORTS void add(const oclMat &a, const oclMat &b, oclMat &c, const oclMat &mask); + CV_EXPORTS void add(const oclMat &a, const oclMat &b, oclMat &c, const oclMat &mask = oclMat()); //! adds scalar to a matrix (c = a + s) // supports all types except CV_8SC1,CV_8SC2,CV8SC3 and CV_8SC4 CV_EXPORTS void add(const oclMat &a, const Scalar &sc, oclMat &c, const oclMat &mask = oclMat()); + //! subtracts one matrix from another (c = a - b) // supports all types except CV_8SC1,CV_8SC2,CV8SC3 and CV_8SC4 - CV_EXPORTS void subtract(const oclMat &a, const oclMat &b, oclMat &c); - //! subtracts one matrix from another (c = a - b) - // supports all types except CV_8SC1,CV_8SC2,CV8SC3 and CV_8SC4 - CV_EXPORTS void subtract(const oclMat &a, const oclMat &b, oclMat &c, const oclMat &mask); + CV_EXPORTS void subtract(const oclMat &a, const oclMat &b, oclMat &c, const oclMat &mask = oclMat()); //! subtracts scalar from a matrix (c = a - s) // supports all types except CV_8SC1,CV_8SC2,CV8SC3 and CV_8SC4 CV_EXPORTS void subtract(const oclMat &a, const Scalar &sc, oclMat &c, const oclMat &mask = oclMat()); - //! subtracts scalar from a matrix (c = a - s) - // supports all types except CV_8SC1,CV_8SC2,CV8SC3 and CV_8SC4 - CV_EXPORTS void subtract(const Scalar &sc, const oclMat &a, oclMat &c, const oclMat &mask = oclMat()); + //! computes element-wise product of the two arrays (c = a * b) // supports all types except CV_8SC1,CV_8SC2,CV8SC3 and CV_8SC4 CV_EXPORTS void multiply(const oclMat &a, const oclMat &b, oclMat &c, double scale = 1); //! multiplies matrix to a number (dst = scalar * src) // supports CV_32FC1 only CV_EXPORTS void multiply(double scalar, const oclMat &src, oclMat &dst); + //! computes element-wise quotient of the two arrays (c = a / b) // supports all types except CV_8SC1,CV_8SC2,CV8SC3 and CV_8SC4 CV_EXPORTS void divide(const oclMat &a, const oclMat &b, oclMat &c, double scale = 1); diff --git a/modules/ocl/src/arithm.cpp b/modules/ocl/src/arithm.cpp index 0cc803d199..03c314c7ca 100644 --- a/modules/ocl/src/arithm.cpp +++ b/modules/ocl/src/arithm.cpp @@ -62,11 +62,11 @@ namespace cv { namespace ocl { - ////////////////////////////////OpenCL kernel strings///////////////////// + //////////////////////////////// OpenCL kernel strings ///////////////////// + extern const char *transpose_kernel; extern const char *arithm_nonzero; extern const char *arithm_sum; - extern const char *arithm_2_mat; extern const char *arithm_sum_3; extern const char *arithm_minMax; extern const char *arithm_minMax_mask; @@ -74,6 +74,7 @@ namespace cv extern const char *arithm_minMaxLoc_mask; extern const char *arithm_LUT; extern const char *arithm_add; + extern const char *arithm_add_mask; extern const char *arithm_add_scalar; extern const char *arithm_add_scalar_mask; extern const char *arithm_bitwise_binary; @@ -83,9 +84,7 @@ namespace cv extern const char *arithm_bitwise_not; extern const char *arithm_compare_eq; extern const char *arithm_compare_ne; - extern const char *arithm_mul; - extern const char *arithm_div; - extern const char *arithm_absdiff; + extern const char *arithm_magnitudeSqr; extern const char *arithm_transpose; extern const char *arithm_flip; extern const char *arithm_flip_rc; @@ -97,390 +96,176 @@ namespace cv extern const char *arithm_addWeighted; extern const char *arithm_phase; extern const char *arithm_pow; - extern const char *arithm_magnitudeSqr; extern const char *arithm_setidentity; - //extern const char * jhp_transpose_kernel; - int64 kernelrealtotal = 0; - int64 kernelalltotal = 0; - int64 reducetotal = 0; - int64 downloadtotal = 0; - int64 alltotal = 0; } } - ////////////////////////////////////////////////////////////////////////////// /////////////////////// add subtract multiply divide ///////////////////////// ////////////////////////////////////////////////////////////////////////////// -template -void arithmetic_run(const oclMat &src1, const oclMat &src2, oclMat &dst, - string kernelName, const char **kernelString, void *_scalar, int op_type = 0) -{ - if(!src1.clCxt->supportsFeature(Context::CL_DOUBLE) && src1.type() == CV_64F) - { - CV_Error(CV_GpuNotSupported, "Selected device don't support double\r\n"); - return; - } - - dst.create(src1.size(), src1.type()); - CV_Assert(src1.cols == src2.cols && src2.cols == dst.cols && - src1.rows == src2.rows && src2.rows == dst.rows); - CV_Assert(src1.type() == src2.type() && src1.type() == dst.type()); - CV_Assert(src1.depth() != CV_8S); - - Context *clCxt = src1.clCxt; - int channels = dst.oclchannels(); - int depth = dst.depth(); - - int vector_lengths[4][7] = {{4, 0, 4, 4, 1, 1, 1}, - {4, 0, 4, 4, 1, 1, 1}, - {4, 0, 4, 4, 1, 1, 1}, - {4, 0, 4, 4, 1, 1, 1} - }; - - size_t vector_length = vector_lengths[channels - 1][depth]; - int offset_cols = (dst.offset / dst.elemSize1()) & (vector_length - 1); - int cols = divUp(dst.cols * channels + offset_cols, vector_length); +////////////////////////////////////////////////////////////////////////////// +/////////////////////// add subtract multiply divide ///////////////////////// +////////////////////////////////////////////////////////////////////////////// - size_t localThreads[3] = { 64, 4, 1 }; - size_t globalThreads[3] = { cols, dst.rows, 1 }; +enum { ADD = 0, SUB, MUL, DIV, ABS_DIFF }; - int dst_step1 = dst.cols * dst.elemSize(); - vector > args; - args.push_back( make_pair( sizeof(cl_mem), (void *)&src1.data )); - args.push_back( make_pair( sizeof(cl_int), (void *)&src1.step )); - args.push_back( make_pair( sizeof(cl_int), (void *)&src1.offset )); - args.push_back( make_pair( sizeof(cl_mem), (void *)&src2.data )); - args.push_back( make_pair( sizeof(cl_int), (void *)&src2.step )); - args.push_back( make_pair( sizeof(cl_int), (void *)&src2.offset )); - args.push_back( make_pair( sizeof(cl_mem), (void *)&dst.data )); - args.push_back( make_pair( sizeof(cl_int), (void *)&dst.step )); - args.push_back( make_pair( sizeof(cl_int), (void *)&dst.offset )); - args.push_back( make_pair( sizeof(cl_int), (void *)&src1.rows )); - args.push_back( make_pair( sizeof(cl_int), (void *)&cols )); - args.push_back( make_pair( sizeof(cl_int), (void *)&dst_step1 )); - T scalar; - if(_scalar != NULL) - { - double scalar1 = *((double *)_scalar); - scalar = (T)scalar1; - args.push_back( make_pair( sizeof(T), (void *)&scalar )); - } - switch(op_type) - { - case MAT_ADD: - openCLExecuteKernel(clCxt, kernelString, kernelName, globalThreads, localThreads, args, -1, depth, "-D ARITHM_ADD"); - break; - case MAT_SUB: - openCLExecuteKernel(clCxt, kernelString, kernelName, globalThreads, localThreads, args, -1, depth, "-D ARITHM_SUB"); - break; - default: - openCLExecuteKernel(clCxt, kernelString, kernelName, globalThreads, localThreads, args, -1, depth); - } -} -static void arithmetic_run(const oclMat &src1, const oclMat &src2, oclMat &dst, - string kernelName, const char **kernelString, int op_type = 0) +static void arithmetic_run_generic(const oclMat &src1, const oclMat &src2, const Scalar & scalar, const oclMat & mask, + oclMat &dst, int op_type, bool use_scalar = false) { - arithmetic_run(src1, src2, dst, kernelName, kernelString, (void *)NULL, op_type); -} -static void arithmetic_run(const oclMat &src1, const oclMat &src2, oclMat &dst, const oclMat &mask, - string kernelName, const char **kernelString, int op_type = 0) -{ - if(!src1.clCxt->supportsFeature(Context::CL_DOUBLE) && src1.type() == CV_64F) + Context *clCxt = src1.clCxt; + bool hasDouble = clCxt->supportsFeature(Context::CL_DOUBLE); + if (!hasDouble && (src1.depth() == CV_64F || src2.depth() == CV_64F || dst.depth() == CV_64F)) { - CV_Error(CV_GpuNotSupported, "Selected device don't support double\r\n"); + CV_Error(CV_GpuNotSupported, "Selected device doesn't support double\r\n"); return; } - dst.create(src1.size(), src1.type()); - CV_Assert(src1.cols == src2.cols && src2.cols == dst.cols && - src1.rows == src2.rows && src2.rows == dst.rows && - src1.rows == mask.rows && src1.cols == mask.cols); - - CV_Assert(src1.type() == src2.type() && src1.type() == dst.type()); - CV_Assert(src1.depth() != CV_8S); - CV_Assert(mask.type() == CV_8U); + CV_Assert(src2.empty() || (!src2.empty() && src1.type() == src2.type() && src1.size() == src2.size())); + CV_Assert(mask.empty() || (!mask.empty() && mask.type() == CV_8UC1 && mask.size() == src1.size())); + CV_Assert(op_type >= ADD && op_type <= ABS_DIFF); - Context *clCxt = src1.clCxt; - int channels = dst.oclchannels(); - int depth = dst.depth(); + dst.create(src1.size(), src1.type()); - int vector_lengths[4][7] = {{4, 4, 2, 2, 1, 1, 1}, - {2, 2, 1, 1, 1, 1, 1}, - {4, 4, 2, 2 , 1, 1, 1}, - {1, 1, 1, 1, 1, 1, 1} - }; + int oclChannels = src1.oclchannels(), depth = src1.depth(); + int src1step1 = src1.step / src1.elemSize(), src1offset1 = src1.offset / src1.elemSize(); + int src2step1 = src2.step / src2.elemSize(), src2offset1 = src2.offset / src2.elemSize(); + int maskstep1 = mask.step, maskoffset1 = mask.offset / mask.elemSize(); + int dststep1 = dst.step / dst.elemSize(), dstoffset1 = dst.offset / dst.elemSize(); + oclMat m; - size_t vector_length = vector_lengths[channels - 1][depth]; - int offset_cols = ((dst.offset % dst.step) / dst.elemSize()) & (vector_length - 1); - int cols = divUp(dst.cols + offset_cols, vector_length); + size_t localThreads[3] = { 16, 16, 1 }; + size_t globalThreads[3] = { dst.cols, dst.rows, 1 }; - size_t localThreads[3] = { 64, 4, 1 }; - size_t globalThreads[3] = { cols, dst.rows, 1 }; + std::string kernelName = op_type == ABS_DIFF ? "arithm_absdiff" : "arithm_binary_op"; + + const char * const typeMap[] = { "uchar", "char", "ushort", "short", "int", "float", "double" }; + const char * const WTypeMap[] = { "short", "short", "int", "int", "int", "float", "double" }; + const char operationsMap[] = { '+', '-', '*', '/', '-' }; + const char * const channelMap[] = { "", "", "2", "4", "4" }; + bool haveScalar = use_scalar || src2.empty(); + + int WDepth = depth; + if (haveScalar) + WDepth = hasDouble && WDepth == CV_64F ? CV_64F : CV_32F; + if (op_type == DIV) + WDepth = hasDouble ? CV_64F : CV_32F; + else if (op_type == MUL) + WDepth = hasDouble && (depth == CV_32S || depth == CV_64F) ? CV_64F : CV_32F; + + std::string buildOptions = format("-D T=%s%s -D WT=%s%s -D convertToT=convert_%s%s%s -D Operation=%c" + " -D convertToWT=convert_%s%s", + typeMap[depth], channelMap[oclChannels], + WTypeMap[WDepth], channelMap[oclChannels], + typeMap[depth], channelMap[oclChannels], (depth >= CV_32F ? "" : (depth == CV_32S ? "_rte" : "_sat_rte")), + operationsMap[op_type], WTypeMap[WDepth], channelMap[oclChannels]); - int dst_step1 = dst.cols * dst.elemSize(); vector > args; args.push_back( make_pair( sizeof(cl_mem), (void *)&src1.data )); - args.push_back( make_pair( sizeof(cl_int), (void *)&src1.step )); - args.push_back( make_pair( sizeof(cl_int), (void *)&src1.offset )); - args.push_back( make_pair( sizeof(cl_mem), (void *)&src2.data )); - args.push_back( make_pair( sizeof(cl_int), (void *)&src2.step )); - args.push_back( make_pair( sizeof(cl_int), (void *)&src2.offset )); - args.push_back( make_pair( sizeof(cl_mem), (void *)&mask.data )); - args.push_back( make_pair( sizeof(cl_int), (void *)&mask.step )); - args.push_back( make_pair( sizeof(cl_int), (void *)&mask.offset )); - args.push_back( make_pair( sizeof(cl_mem), (void *)&dst.data )); - args.push_back( make_pair( sizeof(cl_int), (void *)&dst.step )); - args.push_back( make_pair( sizeof(cl_int), (void *)&dst.offset )); - args.push_back( make_pair( sizeof(cl_int), (void *)&src1.rows )); - args.push_back( make_pair( sizeof(cl_int), (void *)&cols )); - args.push_back( make_pair( sizeof(cl_int), (void *)&dst_step1 )); + args.push_back( make_pair( sizeof(cl_int), (void *)&src1step1 )); + args.push_back( make_pair( sizeof(cl_int), (void *)&src1offset1 )); - switch (op_type) + if (!src2.empty()) { - case MAT_ADD: - openCLExecuteKernel(clCxt, kernelString, kernelName, globalThreads, localThreads, args, channels, depth, "-D ARITHM_ADD"); - break; - case MAT_SUB: - openCLExecuteKernel(clCxt, kernelString, kernelName, globalThreads, localThreads, args, channels, depth, "-D ARITHM_SUB"); - break; - default: - openCLExecuteKernel(clCxt, kernelString, kernelName, globalThreads, localThreads, args, channels, depth); - } -} -void cv::ocl::add(const oclMat &src1, const oclMat &src2, oclMat &dst) -{ - arithmetic_run(src1, src2, dst, "arithm_add", &arithm_add, MAT_ADD); -} -void cv::ocl::add(const oclMat &src1, const oclMat &src2, oclMat &dst, const oclMat &mask) -{ - arithmetic_run(src1, src2, dst, mask, "arithm_add_with_mask", &arithm_add, MAT_ADD); -} - -void cv::ocl::subtract(const oclMat &src1, const oclMat &src2, oclMat &dst) -{ - arithmetic_run(src1, src2, dst, "arithm_add", &arithm_add, MAT_SUB); -} -void cv::ocl::subtract(const oclMat &src1, const oclMat &src2, oclMat &dst, const oclMat &mask) -{ - arithmetic_run(src1, src2, dst, mask, "arithm_add_with_mask", &arithm_add, MAT_SUB); -} -typedef void (*MulDivFunc)(const oclMat &src1, const oclMat &src2, oclMat &dst, string kernelName, - const char **kernelString, void *scalar); - -void cv::ocl::multiply(const oclMat &src1, const oclMat &src2, oclMat &dst, double scalar) -{ - if(src1.clCxt->supportsFeature(Context::CL_DOUBLE) && (src1.depth() == CV_64F)) - arithmetic_run(src1, src2, dst, "arithm_mul", &arithm_mul, (void *)(&scalar)); - else - arithmetic_run(src1, src2, dst, "arithm_mul", &arithm_mul, (void *)(&scalar)); -} - -void cv::ocl::divide(const oclMat &src1, const oclMat &src2, oclMat &dst, double scalar) -{ - - if(src1.clCxt->supportsFeature(Context::CL_DOUBLE)) - arithmetic_run(src1, src2, dst, "arithm_div", &arithm_div, (void *)(&scalar)); - else - arithmetic_run(src1, src2, dst, "arithm_div", &arithm_div, (void *)(&scalar)); + args.push_back( make_pair( sizeof(cl_mem), (void *)&src2.data )); + args.push_back( make_pair( sizeof(cl_int), (void *)&src2step1 )); + args.push_back( make_pair( sizeof(cl_int), (void *)&src2offset1 )); -} -template -void arithmetic_scalar_run(const oclMat &src1, const Scalar &src2, oclMat &dst, const oclMat &mask, string kernelName, const char **kernelString, int isMatSubScalar) -{ - if(!src1.clCxt->supportsFeature(Context::CL_DOUBLE) && src1.type() == CV_64F) - { - CV_Error(CV_GpuNotSupported, "Selected device don't support double\r\n"); - return; + kernelName += "_mat"; } - dst.create(src1.size(), src1.type()); - - CV_Assert(src1.cols == dst.cols && src1.rows == dst.rows && - src1.type() == dst.type()); - - //CV_Assert(src1.depth() != CV_8S); - - if(mask.data) + if (haveScalar) { - CV_Assert(mask.type() == CV_8U && src1.rows == mask.rows && src1.cols == mask.cols); - } - - Context *clCxt = src1.clCxt; - int channels = dst.oclchannels(); - int depth = dst.depth(); - - WT s[4] = { saturate_cast(src2.val[0]), saturate_cast(src2.val[1]), - saturate_cast(src2.val[2]), saturate_cast(src2.val[3]) - }; + const int WDepthMap[] = { CV_16S, CV_16S, CV_32S, CV_32S, CV_32S, CV_32F, CV_64F }; + m.create(1, 1, CV_MAKE_TYPE(WDepthMap[WDepth], oclChannels)); + m.setTo(scalar); - int vector_lengths[4][7] = {{4, 0, 2, 2, 1, 1, 1}, - {2, 0, 1, 1, 1, 1, 1}, - {4, 0, 2, 2 , 1, 1, 1}, - {1, 0, 1, 1, 1, 1, 1} - }; - - size_t vector_length = vector_lengths[channels - 1][depth]; - int offset_cols = ((dst.offset % dst.step) / dst.elemSize()) & (vector_length - 1); - int cols = divUp(dst.cols + offset_cols, vector_length); - - size_t localThreads[3] = { 64, 4, 1 }; - size_t globalThreads[3] = { cols, dst.rows, 1 }; + args.push_back( make_pair( sizeof(cl_mem), (void *)&m.data )); - int dst_step1 = dst.cols * dst.elemSize(); - vector > args; - args.push_back( make_pair( sizeof(cl_mem) , (void *)&src1.data )); - args.push_back( make_pair( sizeof(cl_int) , (void *)&src1.step )); - args.push_back( make_pair( sizeof(cl_int) , (void *)&src1.offset)); - args.push_back( make_pair( sizeof(cl_mem) , (void *)&dst.data )); - args.push_back( make_pair( sizeof(cl_int) , (void *)&dst.step )); - args.push_back( make_pair( sizeof(cl_int) , (void *)&dst.offset)); - - if(mask.data) - { - args.push_back( make_pair( sizeof(cl_mem) , (void *)&mask.data )); - args.push_back( make_pair( sizeof(cl_int) , (void *)&mask.step )); - args.push_back( make_pair( sizeof(cl_int) , (void *)&mask.offset)); + kernelName += "_scalar"; } - args.push_back( make_pair( sizeof(CL_WT) , (void *)&s )); - args.push_back( make_pair( sizeof(cl_int) , (void *)&src1.rows )); - args.push_back( make_pair( sizeof(cl_int) , (void *)&cols )); - args.push_back( make_pair( sizeof(cl_int) , (void *)&dst_step1 )); - if(isMatSubScalar != 0) - openCLExecuteKernel(clCxt, kernelString, kernelName, globalThreads, localThreads, args, channels, depth, "-D ARITHM_SUB"); - else - openCLExecuteKernel(clCxt, kernelString, kernelName, globalThreads, localThreads, args, channels, depth, "-D ARITHM_ADD"); -} -static void arithmetic_scalar_run(const oclMat &src, oclMat &dst, string kernelName, const char **kernelString, double scalar) -{ - if(!src.clCxt->supportsFeature(Context::CL_DOUBLE) && src.type() == CV_64F) + if (!mask.empty()) { - CV_Error(CV_GpuNotSupported, "Selected device don't support double\r\n"); - return; - } - - dst.create(src.size(), src.type()); - CV_Assert(src.cols == dst.cols && src.rows == dst.rows); - - CV_Assert(src.type() == dst.type()); - CV_Assert(src.depth() != CV_8S); - - Context *clCxt = src.clCxt; - int channels = dst.oclchannels(); - int depth = dst.depth(); - - int vector_lengths[4][7] = {{4, 0, 4, 4, 1, 1, 1}, - {4, 0, 4, 4, 1, 1, 1}, - {4, 0, 4, 4 , 1, 1, 1}, - {4, 0, 4, 4, 1, 1, 1} - }; + args.push_back( make_pair( sizeof(cl_mem), (void *)&mask.data )); + args.push_back( make_pair( sizeof(cl_int), (void *)&maskstep1 )); + args.push_back( make_pair( sizeof(cl_int), (void *)&maskoffset1 )); - size_t vector_length = vector_lengths[channels - 1][depth]; - int offset_cols = (dst.offset / dst.elemSize1()) & (vector_length - 1); - int cols = divUp(dst.cols * channels + offset_cols, vector_length); + kernelName += "_mask"; + } - size_t localThreads[3] = { 64, 4, 1 }; - size_t globalThreads[3] = { cols, dst.rows, 1 }; + if (op_type == DIV) + kernelName += "_div"; - int dst_step1 = dst.cols * dst.elemSize(); - vector > args; - args.push_back( make_pair( sizeof(cl_mem), (void *)&src.data )); - args.push_back( make_pair( sizeof(cl_int), (void *)&src.step )); - args.push_back( make_pair( sizeof(cl_int), (void *)&src.offset )); args.push_back( make_pair( sizeof(cl_mem), (void *)&dst.data )); - args.push_back( make_pair( sizeof(cl_int), (void *)&dst.step )); - args.push_back( make_pair( sizeof(cl_int), (void *)&dst.offset )); - args.push_back( make_pair( sizeof(cl_int), (void *)&src.rows )); - args.push_back( make_pair( sizeof(cl_int), (void *)&cols )); - args.push_back( make_pair( sizeof(cl_int), (void *)&dst_step1 )); + args.push_back( make_pair( sizeof(cl_int), (void *)&dststep1 )); + args.push_back( make_pair( sizeof(cl_int), (void *)&dstoffset1 )); - float f_scalar = (float)scalar; - if(src.clCxt->supportsFeature(Context::CL_DOUBLE)) - args.push_back( make_pair( sizeof(cl_double), (void *)&scalar )); - else - { - args.push_back( make_pair( sizeof(cl_float), (void *)&f_scalar)); - } + args.push_back( make_pair( sizeof(cl_int), (void *)&src1.cols )); + args.push_back( make_pair( sizeof(cl_int), (void *)&src1.rows )); - openCLExecuteKernel(clCxt, kernelString, kernelName, globalThreads, localThreads, args, -1, depth); + openCLExecuteKernel(clCxt, mask.empty() ? + (!src2.empty() ? &arithm_add : &arithm_add_scalar) : + (!src2.empty() ? &arithm_add_mask : &arithm_add_scalar_mask), + kernelName, globalThreads, localThreads, + args, -1, -1, buildOptions.c_str()); } -typedef void (*ArithmeticFuncS)(const oclMat &src1, const Scalar &src2, oclMat &dst, const oclMat &mask, string kernelName, const char **kernelString, int isMatSubScalar); - - -static void arithmetic_scalar(const oclMat &src1, const Scalar &src2, oclMat &dst, const oclMat &mask, string kernelName, const char **kernelString, int isMatSubScalar) -{ - static ArithmeticFuncS tab[8] = - { - arithmetic_scalar_run, - arithmetic_scalar_run, - arithmetic_scalar_run, - arithmetic_scalar_run, - arithmetic_scalar_run, - arithmetic_scalar_run, - arithmetic_scalar_run, - 0 - }; - ArithmeticFuncS func = tab[src1.depth()]; - if(func == 0) - cv::ocl::error("Unsupported arithmetic operation", __FILE__, __LINE__); - func(src1, src2, dst, mask, kernelName, kernelString, isMatSubScalar); -} -static void arithmetic_scalar(const oclMat &src1, const Scalar &src2, oclMat &dst, const oclMat &mask, string kernelName, const char **kernelString) +void cv::ocl::add(const oclMat &src1, const oclMat &src2, oclMat &dst, const oclMat &mask) { - arithmetic_scalar(src1, src2, dst, mask, kernelName, kernelString, 0); + arithmetic_run_generic(src1, src2, Scalar(), mask, dst, ADD); } void cv::ocl::add(const oclMat &src1, const Scalar &src2, oclMat &dst, const oclMat &mask) { - string kernelName = mask.data ? "arithm_s_add_with_mask" : "arithm_s_add"; - const char **kernelString = mask.data ? &arithm_add_scalar_mask : &arithm_add_scalar; + arithmetic_run_generic(src1, oclMat(), src2, mask, dst, ADD); +} - arithmetic_scalar( src1, src2, dst, mask, kernelName, kernelString); +void cv::ocl::subtract(const oclMat &src1, const oclMat &src2, oclMat &dst, const oclMat &mask) +{ + arithmetic_run_generic(src1, src2, Scalar(), mask, dst, SUB); } void cv::ocl::subtract(const oclMat &src1, const Scalar &src2, oclMat &dst, const oclMat &mask) { - string kernelName = mask.data ? "arithm_s_add_with_mask" : "arithm_s_add"; - const char **kernelString = mask.data ? &arithm_add_scalar_mask : &arithm_add_scalar; - arithmetic_scalar( src1, src2, dst, mask, kernelName, kernelString, 1); + arithmetic_run_generic(src1, oclMat(), src2, mask, dst, SUB); } -void cv::ocl::subtract(const Scalar &src2, const oclMat &src1, oclMat &dst, const oclMat &mask) + +void cv::ocl::multiply(const oclMat &src1, const oclMat &src2, oclMat &dst, double scalar) { - string kernelName = mask.data ? "arithm_s_add_with_mask" : "arithm_s_add"; - const char **kernelString = mask.data ? &arithm_add_scalar_mask : &arithm_add_scalar; - arithmetic_scalar( src1, src2, dst, mask, kernelName, kernelString, -1); + const bool use_scalar = !(std::abs(scalar - 1.0) < std::numeric_limits::epsilon()); + arithmetic_run_generic(src1, src2, Scalar::all(scalar), oclMat(), dst, MUL, use_scalar); } + void cv::ocl::multiply(double scalar, const oclMat &src, oclMat &dst) { - string kernelName = "arithm_muls"; - arithmetic_scalar_run( src, dst, kernelName, &arithm_mul, scalar); + arithmetic_run_generic(src, oclMat(), Scalar::all(scalar), oclMat(), dst, MUL); } -void cv::ocl::divide(double scalar, const oclMat &src, oclMat &dst) + +void cv::ocl::divide(const oclMat &src1, const oclMat &src2, oclMat &dst, double scalar) { - if(!src.clCxt->supportsFeature(Context::CL_DOUBLE)) - { - CV_Error(CV_GpuNotSupported, "Selected device don't support double\r\n"); - return; - } + const bool use_scalar = !(std::abs(scalar - 1.0) < std::numeric_limits::epsilon()); + arithmetic_run_generic(src1, src2, Scalar::all(scalar), oclMat(), dst, DIV, use_scalar); +} - string kernelName = "arithm_s_div"; - arithmetic_scalar_run(src, dst, kernelName, &arithm_div, scalar); +void cv::ocl::divide(double scalar, const oclMat &src, oclMat &dst) +{ + arithmetic_run_generic(src, oclMat(), Scalar::all(scalar), oclMat(), dst, DIV); } + ////////////////////////////////////////////////////////////////////////////// ///////////////////////////////// Absdiff /////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// + void cv::ocl::absdiff(const oclMat &src1, const oclMat &src2, oclMat &dst) { - arithmetic_run(src1, src2, dst, "arithm_absdiff", &arithm_absdiff); + arithmetic_run_generic(src1, src2, Scalar(), oclMat(), dst, ABS_DIFF); } + void cv::ocl::absdiff(const oclMat &src1, const Scalar &src2, oclMat &dst) { - string kernelName = "arithm_s_absdiff"; - oclMat mask; - arithmetic_scalar( src1, src2, dst, mask, kernelName, &arithm_absdiff); + arithmetic_run_generic(src1, oclMat(), src2, oclMat(), dst, ABS_DIFF); } + ////////////////////////////////////////////////////////////////////////////// ///////////////////////////////// compare /////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// diff --git a/modules/ocl/src/opencl/arithm_add.cl b/modules/ocl/src/opencl/arithm_add.cl index 070ced4731..38834e7660 100644 --- a/modules/ocl/src/opencl/arithm_add.cl +++ b/modules/ocl/src/opencl/arithm_add.cl @@ -52,809 +52,105 @@ #endif #endif -#ifdef ARITHM_ADD - #define ARITHM_OP(A,B) ((A)+(B)) -#elif defined ARITHM_SUB - #define ARITHM_OP(A,B) ((A)-(B)) -#endif ////////////////////////////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////ADD//////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////////////////////////////// -/**************************************add without mask**************************************/ -__kernel void arithm_add_D0 (__global uchar *src1, int src1_step, int src1_offset, - __global uchar *src2, int src2_step, int src2_offset, - __global uchar *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 2; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align (dst_offset & 3) - int src1_index = mad24(y, src1_step, x + src1_offset - dst_align); - int src2_index = mad24(y, src2_step, x + src2_offset - dst_align); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + x & (int)0xfffffffc); - int src1_index_fix = src1_index < 0 ? 0 : src1_index; - int src2_index_fix = src2_index < 0 ? 0 : src2_index; - uchar4 src1_data = vload4(0, src1 + src1_index_fix); - uchar4 src2_data = vload4(0, src2 + src2_index_fix); - if(src1_index < 0) - { - uchar4 tmp; - tmp.xyzw = (src1_index == -2) ? src1_data.zwxy:src1_data.yzwx; - src1_data.xyzw = (src1_index == -1) ? src1_data.wxyz:tmp.xyzw; - } - if(src2_index < 0) - { - uchar4 tmp; - tmp.xyzw = (src2_index == -2) ? src2_data.zwxy:src2_data.yzwx; - src2_data.xyzw = (src2_index == -1) ? src2_data.wxyz:tmp.xyzw; - } - uchar4 dst_data = *((__global uchar4 *)(dst + dst_index)); - short4 tmp = ARITHM_OP(convert_short4_sat(src1_data), convert_short4_sat(src2_data)); - uchar4 tmp_data = convert_uchar4_sat(tmp); - - dst_data.x = ((dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : dst_data.x; - dst_data.y = ((dst_index + 1 >= dst_start) && (dst_index + 1 < dst_end)) ? tmp_data.y : dst_data.y; - dst_data.z = ((dst_index + 2 >= dst_start) && (dst_index + 2 < dst_end)) ? tmp_data.z : dst_data.z; - dst_data.w = ((dst_index + 3 >= dst_start) && (dst_index + 3 < dst_end)) ? tmp_data.w : dst_data.w; - - *((__global uchar4 *)(dst + dst_index)) = dst_data; - } -} -__kernel void arithm_add_D2 (__global ushort *src1, int src1_step, int src1_offset, - __global ushort *src2, int src2_step, int src2_offset, - __global ushort *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) - -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 2; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align ((dst_offset / 2) & 3) - int src1_index = mad24(y, src1_step, (x << 1) + src1_offset - (dst_align << 1)); - int src2_index = mad24(y, src2_step, (x << 1) + src2_offset - (dst_align << 1)); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + (x << 1) & (int)0xfffffff8); - - ushort4 src1_data = vload4(0, (__global ushort *)((__global char *)src1 + src1_index)); - ushort4 src2_data = vload4(0, (__global ushort *)((__global char *)src2 + src2_index)); - - ushort4 dst_data = *((__global ushort4 *)((__global char *)dst + dst_index)); - int4 tmp = ARITHM_OP(convert_int4_sat(src1_data), convert_int4_sat(src2_data)); - ushort4 tmp_data = convert_ushort4_sat(tmp); - - dst_data.x = ((dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : dst_data.x; - dst_data.y = ((dst_index + 2 >= dst_start) && (dst_index + 2 < dst_end)) ? tmp_data.y : dst_data.y; - dst_data.z = ((dst_index + 4 >= dst_start) && (dst_index + 4 < dst_end)) ? tmp_data.z : dst_data.z; - dst_data.w = ((dst_index + 6 >= dst_start) && (dst_index + 6 < dst_end)) ? tmp_data.w : dst_data.w; - - *((__global ushort4 *)((__global char *)dst + dst_index)) = dst_data; - } -} -__kernel void arithm_add_D3 (__global short *src1, int src1_step, int src1_offset, - __global short *src2, int src2_step, int src2_offset, - __global short *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 2; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align ((dst_offset / 2) & 3) - int src1_index = mad24(y, src1_step, (x << 1) + src1_offset - (dst_align << 1)); - int src2_index = mad24(y, src2_step, (x << 1) + src2_offset - (dst_align << 1)); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + (x << 1) & (int)0xfffffff8); - - short4 src1_data = vload4(0, (__global short *)((__global char *)src1 + src1_index)); - short4 src2_data = vload4(0, (__global short *)((__global char *)src2 + src2_index)); - - short4 dst_data = *((__global short4 *)((__global char *)dst + dst_index)); - int4 tmp = ARITHM_OP(convert_int4_sat(src1_data), convert_int4_sat(src2_data)); - short4 tmp_data = convert_short4_sat(tmp); - - dst_data.x = ((dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : dst_data.x; - dst_data.y = ((dst_index + 2 >= dst_start) && (dst_index + 2 < dst_end)) ? tmp_data.y : dst_data.y; - dst_data.z = ((dst_index + 4 >= dst_start) && (dst_index + 4 < dst_end)) ? tmp_data.z : dst_data.z; - dst_data.w = ((dst_index + 6 >= dst_start) && (dst_index + 6 < dst_end)) ? tmp_data.w : dst_data.w; - - *((__global short4 *)((__global char *)dst + dst_index)) = dst_data; - } -} - -__kernel void arithm_add_D4 (__global int *src1, int src1_step, int src1_offset, - __global int *src2, int src2_step, int src2_offset, - __global int *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 2) + src1_offset); - int src2_index = mad24(y, src2_step, (x << 2) + src2_offset); - int dst_index = mad24(y, dst_step, (x << 2) + dst_offset); - - int data1 = *((__global int *)((__global char *)src1 + src1_index)); - int data2 = *((__global int *)((__global char *)src2 + src2_index)); - long tmp = ARITHM_OP((long)(data1), (long)(data2)); - - *((__global int *)((__global char *)dst + dst_index)) = convert_int_sat(tmp); - } -} -__kernel void arithm_add_D5 (__global float *src1, int src1_step, int src1_offset, - __global float *src2, int src2_step, int src2_offset, - __global float *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 2) + src1_offset); - int src2_index = mad24(y, src2_step, (x << 2) + src2_offset); - int dst_index = mad24(y, dst_step, (x << 2) + dst_offset); - - float data1 = *((__global float *)((__global char *)src1 + src1_index)); - float data2 = *((__global float *)((__global char *)src2 + src2_index)); - float tmp = ARITHM_OP(data1, data2); - - *((__global float *)((__global char *)dst + dst_index)) = tmp; - } -} - -#if defined (DOUBLE_SUPPORT) -__kernel void arithm_add_D6 (__global double *src1, int src1_step, int src1_offset, - __global double *src2, int src2_step, int src2_offset, - __global double *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 3) + src1_offset); - int src2_index = mad24(y, src2_step, (x << 3) + src2_offset); - int dst_index = mad24(y, dst_step, (x << 3) + dst_offset); - - double data1 = *((__global double *)((__global char *)src1 + src1_index)); - double data2 = *((__global double *)((__global char *)src2 + src2_index)); - - *((__global double *)((__global char *)dst + dst_index)) = ARITHM_OP(data1, data2); - } -} -#endif - -/**************************************add with mask**************************************/ -__kernel void arithm_add_with_mask_C1_D0 (__global uchar *src1, int src1_step, int src1_offset, - __global uchar *src2, int src2_step, int src2_offset, - __global uchar *mask, int mask_step, int mask_offset, - __global uchar *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 2; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align (dst_offset & 3) - int src1_index = mad24(y, src1_step, x + src1_offset - dst_align); - int src2_index = mad24(y, src2_step, x + src2_offset - dst_align); - int mask_index = mad24(y, mask_step, x + mask_offset - dst_align); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + x & (int)0xfffffffc); - int src1_index_fix = src1_index < 0 ? 0 : src1_index; - int src2_index_fix = src2_index < 0 ? 0 : src2_index; - int mask_index_fix = mask_index < 0 ? 0 : mask_index; - uchar4 src1_data = vload4(0, src1 + src1_index_fix); - uchar4 src2_data = vload4(0, src2 + src2_index_fix); - uchar4 mask_data = vload4(0, mask + mask_index_fix); - if(src1_index < 0) - { - uchar4 tmp; - tmp.xyzw = (src1_index == -2) ? src1_data.zwxy:src1_data.yzwx; - src1_data.xyzw = (src1_index == -1) ? src1_data.wxyz:tmp.xyzw; - } - if(src2_index < 0) - { - uchar4 tmp; - tmp.xyzw = (src2_index == -2) ? src2_data.zwxy:src2_data.yzwx; - src2_data.xyzw = (src2_index == -1) ? src2_data.wxyz:tmp.xyzw; - } - if(mask_index < 0) - { - uchar4 tmp; - tmp.xyzw = (mask_index == -2) ? mask_data.zwxy:mask_data.yzwx; - mask_data.xyzw = (mask_index == -1) ? mask_data.wxyz:tmp.xyzw; - } - - uchar4 data = *((__global uchar4 *)(dst + dst_index)); - short4 tmp = ARITHM_OP(convert_short4_sat(src1_data), convert_short4_sat(src2_data)); - uchar4 tmp_data = convert_uchar4_sat(tmp); - - data.x = ((mask_data.x) && (dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : data.x; - data.y = ((mask_data.y) && (dst_index + 1 >= dst_start) && (dst_index + 1 < dst_end)) ? tmp_data.y : data.y; - data.z = ((mask_data.z) && (dst_index + 2 >= dst_start) && (dst_index + 2 < dst_end)) ? tmp_data.z : data.z; - data.w = ((mask_data.w) && (dst_index + 3 >= dst_start) && (dst_index + 3 < dst_end)) ? tmp_data.w : data.w; - - *((__global uchar4 *)(dst + dst_index)) = data; - } -} -__kernel void arithm_add_with_mask_C1_D2 (__global ushort *src1, int src1_step, int src1_offset, - __global ushort *src2, int src2_step, int src2_offset, - __global uchar *mask, int mask_step, int mask_offset, - __global ushort *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 1; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align ((dst_offset / 2) & 1) - int src1_index = mad24(y, src1_step, (x << 1) + src1_offset - (dst_align << 1)); - int src2_index = mad24(y, src2_step, (x << 1) + src2_offset - (dst_align << 1)); - int mask_index = mad24(y, mask_step, x + mask_offset - dst_align); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + (x << 1) & (int)0xfffffffc); - - ushort2 src1_data = vload2(0, (__global ushort *)((__global char *)src1 + src1_index)); - ushort2 src2_data = vload2(0, (__global ushort *)((__global char *)src2 + src2_index)); - uchar2 mask_data = vload2(0, mask + mask_index); - - ushort2 data = *((__global ushort2 *)((__global uchar *)dst + dst_index)); - int2 tmp = ARITHM_OP(convert_int2_sat(src1_data), convert_int2_sat(src2_data)); - ushort2 tmp_data = convert_ushort2_sat(tmp); - - data.x = ((mask_data.x) && (dst_index + 0 >= dst_start)) ? tmp_data.x : data.x; - data.y = ((mask_data.y) && (dst_index + 2 < dst_end )) ? tmp_data.y : data.y; - - *((__global ushort2 *)((__global uchar *)dst + dst_index)) = data; - } -} -__kernel void arithm_add_with_mask_C1_D3 (__global short *src1, int src1_step, int src1_offset, - __global short *src2, int src2_step, int src2_offset, - __global uchar *mask, int mask_step, int mask_offset, - __global short *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 1; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align ((dst_offset / 2) & 1) - int src1_index = mad24(y, src1_step, (x << 1) + src1_offset - (dst_align << 1)); - int src2_index = mad24(y, src2_step, (x << 1) + src2_offset - (dst_align << 1)); - int mask_index = mad24(y, mask_step, x + mask_offset - dst_align); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + (x << 1) & (int)0xfffffffc); - - short2 src1_data = vload2(0, (__global short *)((__global char *)src1 + src1_index)); - short2 src2_data = vload2(0, (__global short *)((__global char *)src2 + src2_index)); - uchar2 mask_data = vload2(0, mask + mask_index); - - short2 data = *((__global short2 *)((__global uchar *)dst + dst_index)); - int2 tmp = ARITHM_OP(convert_int2_sat(src1_data), convert_int2_sat(src2_data)); - short2 tmp_data = convert_short2_sat(tmp); - - data.x = ((mask_data.x) && (dst_index + 0 >= dst_start)) ? tmp_data.x : data.x; - data.y = ((mask_data.y) && (dst_index + 2 < dst_end )) ? tmp_data.y : data.y; - - *((__global short2 *)((__global uchar *)dst + dst_index)) = data; - } -} -__kernel void arithm_add_with_mask_C1_D4 (__global int *src1, int src1_step, int src1_offset, - __global int *src2, int src2_step, int src2_offset, - __global uchar *mask, int mask_step, int mask_offset, - __global int *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 2) + src1_offset); - int src2_index = mad24(y, src2_step, (x << 2) + src2_offset); - int mask_index = mad24(y, mask_step, x + mask_offset); - int dst_index = mad24(y, dst_step, (x << 2) + dst_offset); - - uchar mask_data = *(mask + mask_index); - - int src_data1 = *((__global int *)((__global char *)src1 + src1_index)); - int src_data2 = *((__global int *)((__global char *)src2 + src2_index)); - int dst_data = *((__global int *)((__global char *)dst + dst_index)); - - int data = convert_int_sat(ARITHM_OP((long)src_data1, (long)src_data2)); - data = mask_data ? data : dst_data; - - *((__global int *)((__global char *)dst + dst_index)) = data; - } -} - -__kernel void arithm_add_with_mask_C1_D5 (__global float *src1, int src1_step, int src1_offset, - __global float *src2, int src2_step, int src2_offset, - __global uchar *mask, int mask_step, int mask_offset, - __global float *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 2) + src1_offset); - int src2_index = mad24(y, src2_step, (x << 2) + src2_offset); - int mask_index = mad24(y, mask_step, x + mask_offset); - int dst_index = mad24(y, dst_step, (x << 2) + dst_offset); - - uchar mask_data = *(mask + mask_index); - - float src_data1 = *((__global float *)((__global char *)src1 + src1_index)); - float src_data2 = *((__global float *)((__global char *)src2 + src2_index)); - float dst_data = *((__global float *)((__global char *)dst + dst_index)); - - float data = ARITHM_OP(src_data1, src_data2); - data = mask_data ? data : dst_data; - - *((__global float *)((__global char *)dst + dst_index)) = data; - } -} - -#if defined (DOUBLE_SUPPORT) -__kernel void arithm_add_with_mask_C1_D6 (__global double *src1, int src1_step, int src1_offset, - __global double *src2, int src2_step, int src2_offset, - __global uchar *mask, int mask_step, int mask_offset, - __global double *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 3) + src1_offset); - int src2_index = mad24(y, src2_step, (x << 3) + src2_offset); - int mask_index = mad24(y, mask_step, x + mask_offset); - int dst_index = mad24(y, dst_step, (x << 3) + dst_offset); - - uchar mask_data = *(mask + mask_index); - - double src_data1 = *((__global double *)((__global char *)src1 + src1_index)); - double src_data2 = *((__global double *)((__global char *)src2 + src2_index)); - double dst_data = *((__global double *)((__global char *)dst + dst_index)); - - double data = ARITHM_OP(src_data1, src_data2); - data = mask_data ? data : dst_data; - - *((__global double *)((__global char *)dst + dst_index)) = data; - } -} -#endif - -__kernel void arithm_add_with_mask_C2_D0 (__global uchar *src1, int src1_step, int src1_offset, - __global uchar *src2, int src2_step, int src2_offset, - __global uchar *mask, int mask_step, int mask_offset, - __global uchar *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 1; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align ((dst_offset / 2) & 1) - int src1_index = mad24(y, src1_step, (x << 1) + src1_offset - (dst_align << 1)); - int src2_index = mad24(y, src2_step, (x << 1) + src2_offset - (dst_align << 1)); - int mask_index = mad24(y, mask_step, x + mask_offset - dst_align); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + (x << 1) & (int)0xfffffffc); - - uchar4 src1_data = vload4(0, src1 + src1_index); - uchar4 src2_data = vload4(0, src2 + src2_index); - uchar2 mask_data = vload2(0, mask + mask_index); - - uchar4 data = *((__global uchar4 *)(dst + dst_index)); - short4 tmp = ARITHM_OP(convert_short4_sat(src1_data), convert_short4_sat(src2_data)); - uchar4 tmp_data = convert_uchar4_sat(tmp); - - data.xy = ((mask_data.x) && (dst_index + 0 >= dst_start)) ? tmp_data.xy : data.xy; - data.zw = ((mask_data.y) && (dst_index + 2 < dst_end )) ? tmp_data.zw : data.zw; - - *((__global uchar4 *)(dst + dst_index)) = data; - } -} -__kernel void arithm_add_with_mask_C2_D2 (__global ushort *src1, int src1_step, int src1_offset, - __global ushort *src2, int src2_step, int src2_offset, - __global uchar *mask, int mask_step, int mask_offset, - __global ushort *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 2) + src1_offset); - int src2_index = mad24(y, src2_step, (x << 2) + src2_offset); - int mask_index = mad24(y, mask_step, x + mask_offset); - int dst_index = mad24(y, dst_step, (x << 2) + dst_offset); - - uchar mask_data = *(mask + mask_index); - - ushort2 src_data1 = *((__global ushort2 *)((__global char *)src1 + src1_index)); - ushort2 src_data2 = *((__global ushort2 *)((__global char *)src2 + src2_index)); - ushort2 dst_data = *((__global ushort2 *)((__global char *)dst + dst_index)); - - int2 tmp = ARITHM_OP(convert_int2_sat(src_data1), convert_int2_sat(src_data2)); - ushort2 data = convert_ushort2_sat(tmp); - data = mask_data ? data : dst_data; - - *((__global ushort2 *)((__global char *)dst + dst_index)) = data; - } -} -__kernel void arithm_add_with_mask_C2_D3 (__global short *src1, int src1_step, int src1_offset, - __global short *src2, int src2_step, int src2_offset, - __global uchar *mask, int mask_step, int mask_offset, - __global short *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 2) + src1_offset); - int src2_index = mad24(y, src2_step, (x << 2) + src2_offset); - int mask_index = mad24(y, mask_step, x + mask_offset); - int dst_index = mad24(y, dst_step, (x << 2) + dst_offset); - - uchar mask_data = *(mask + mask_index); - - short2 src_data1 = *((__global short2 *)((__global char *)src1 + src1_index)); - short2 src_data2 = *((__global short2 *)((__global char *)src2 + src2_index)); - short2 dst_data = *((__global short2 *)((__global char *)dst + dst_index)); - - int2 tmp = ARITHM_OP(convert_int2_sat(src_data1), convert_int2_sat(src_data2)); - short2 data = convert_short2_sat(tmp); - data = mask_data ? data : dst_data; - - *((__global short2 *)((__global char *)dst + dst_index)) = data; - } -} -__kernel void arithm_add_with_mask_C2_D4 (__global int *src1, int src1_step, int src1_offset, - __global int *src2, int src2_step, int src2_offset, - __global uchar *mask, int mask_step, int mask_offset, - __global int *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 3) + src1_offset); - int src2_index = mad24(y, src2_step, (x << 3) + src2_offset); - int mask_index = mad24(y, mask_step, x + mask_offset); - int dst_index = mad24(y, dst_step, (x << 3) + dst_offset); - - uchar mask_data = *(mask + mask_index); - - int2 src_data1 = *((__global int2 *)((__global char *)src1 + src1_index)); - int2 src_data2 = *((__global int2 *)((__global char *)src2 + src2_index)); - int2 dst_data = *((__global int2 *)((__global char *)dst + dst_index)); - - int2 data = convert_int2_sat(ARITHM_OP(convert_long2_sat(src_data1), convert_long2_sat(src_data2))); - data = mask_data ? data : dst_data; - - *((__global int2 *)((__global char *)dst + dst_index)) = data; - } -} -__kernel void arithm_add_with_mask_C2_D5 (__global float *src1, int src1_step, int src1_offset, - __global float *src2, int src2_step, int src2_offset, - __global uchar *mask, int mask_step, int mask_offset, - __global float *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 3) + src1_offset); - int src2_index = mad24(y, src2_step, (x << 3) + src2_offset); - int mask_index = mad24(y, mask_step, x + mask_offset); - int dst_index = mad24(y, dst_step, (x << 3) + dst_offset); - - uchar mask_data = *(mask + mask_index); - - float2 src_data1 = *((__global float2 *)((__global char *)src1 + src1_index)); - float2 src_data2 = *((__global float2 *)((__global char *)src2 + src2_index)); - float2 dst_data = *((__global float2 *)((__global char *)dst + dst_index)); - - float2 data = ARITHM_OP(src_data1, src_data2); - data = mask_data ? data : dst_data; - - *((__global float2 *)((__global char *)dst + dst_index)) = data; - } -} +///////////////////////////////////////////// ADD //////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////// -#if defined (DOUBLE_SUPPORT) -__kernel void arithm_add_with_mask_C2_D6 (__global double *src1, int src1_step, int src1_offset, - __global double *src2, int src2_step, int src2_offset, - __global uchar *mask, int mask_step, int mask_offset, - __global double *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) +__kernel void arithm_binary_op_mat(__global T *src1, int src1_step, int src1_offset, + __global T *src2, int src2_step, int src2_offset, + __global T *dst, int dst_step, int dst_offset, + int cols, int rows) { - int x = get_global_id(0); int y = get_global_id(1); if (x < cols && y < rows) { - int src1_index = mad24(y, src1_step, (x << 4) + src1_offset); - int src2_index = mad24(y, src2_step, (x << 4) + src2_offset); - int mask_index = mad24(y, mask_step, x + mask_offset); - int dst_index = mad24(y, dst_step, (x << 4) + dst_offset); - - uchar mask_data = *(mask + mask_index); + int src1_index = mad24(y, src1_step, x + src1_offset); + int src2_index = mad24(y, src2_step, x + src2_offset); + int dst_index = mad24(y, dst_step, x + dst_offset); - double2 src_data1 = *((__global double2 *)((__global char *)src1 + src1_index)); - double2 src_data2 = *((__global double2 *)((__global char *)src2 + src2_index)); - double2 dst_data = *((__global double2 *)((__global char *)dst + dst_index)); - - double2 data = ARITHM_OP(src_data1, src_data2); - data = mask_data ? data : dst_data; - - *((__global double2 *)((__global char *)dst + dst_index)) = data; + dst[dst_index] = convertToT(convertToWT(src1[src1_index]) Operation convertToWT(src2[src2_index])); } } -#endif -__kernel void arithm_add_with_mask_C4_D0 (__global uchar *src1, int src1_step, int src1_offset, - __global uchar *src2, int src2_step, int src2_offset, - __global uchar *mask, int mask_step, int mask_offset, - __global uchar *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) +__kernel void arithm_binary_op_mat_div(__global T *src1, int src1_step, int src1_offset, + __global T *src2, int src2_step, int src2_offset, + __global T *dst, int dst_step, int dst_offset, + int cols, int rows) { - int x = get_global_id(0); int y = get_global_id(1); if (x < cols && y < rows) { - int src1_index = mad24(y, src1_step, (x << 2) + src1_offset); - int src2_index = mad24(y, src2_step, (x << 2) + src2_offset); - int mask_index = mad24(y, mask_step, x + mask_offset); - int dst_index = mad24(y, dst_step, (x << 2) + dst_offset); - - uchar mask_data = *(mask + mask_index); + int src1_index = mad24(y, src1_step, x + src1_offset); + int src2_index = mad24(y, src2_step, x + src2_offset); + int dst_index = mad24(y, dst_step, x + dst_offset); - uchar4 src_data1 = *((__global uchar4 *)(src1 + src1_index)); - uchar4 src_data2 = *((__global uchar4 *)(src2 + src2_index)); - uchar4 dst_data = *((__global uchar4 *)(dst + dst_index)); - - uchar4 data = convert_uchar4_sat(ARITHM_OP(convert_short4_sat(src_data1), convert_short4_sat(src_data2))); - data = mask_data ? data : dst_data; - - *((__global uchar4 *)(dst + dst_index)) = data; + T zero = (T)(0); + dst[dst_index] = src2[src2_index] == zero ? zero : convertToT(convertToWT(src1[src1_index]) / convertToWT(src2[src2_index])); } } -__kernel void arithm_add_with_mask_C4_D2 (__global ushort *src1, int src1_step, int src1_offset, - __global ushort *src2, int src2_step, int src2_offset, - __global uchar *mask, int mask_step, int mask_offset, - __global ushort *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 3) + src1_offset); - int src2_index = mad24(y, src2_step, (x << 3) + src2_offset); - int mask_index = mad24(y, mask_step, x + mask_offset); - int dst_index = mad24(y, dst_step, (x << 3) + dst_offset); - uchar mask_data = *(mask + mask_index); - - ushort4 src_data1 = *((__global ushort4 *)((__global char *)src1 + src1_index)); - ushort4 src_data2 = *((__global ushort4 *)((__global char *)src2 + src2_index)); - ushort4 dst_data = *((__global ushort4 *)((__global char *)dst + dst_index)); - - ushort4 data = convert_ushort4_sat(ARITHM_OP(convert_int4_sat(src_data1), convert_int4_sat(src_data2))); - data = mask_data ? data : dst_data; - - *((__global ushort4 *)((__global char *)dst + dst_index)) = data; - } -} -__kernel void arithm_add_with_mask_C4_D3 (__global short *src1, int src1_step, int src1_offset, - __global short *src2, int src2_step, int src2_offset, - __global uchar *mask, int mask_step, int mask_offset, - __global short *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) +__kernel void arithm_absdiff_mat(__global T *src1, int src1_step, int src1_offset, + __global T *src2, int src2_step, int src2_offset, + __global T *dst, int dst_step, int dst_offset, + int cols, int rows) { - int x = get_global_id(0); int y = get_global_id(1); if (x < cols && y < rows) { - int src1_index = mad24(y, src1_step, (x << 3) + src1_offset); - int src2_index = mad24(y, src2_step, (x << 3) + src2_offset); - int mask_index = mad24(y, mask_step, x + mask_offset); - int dst_index = mad24(y, dst_step, (x << 3) + dst_offset); - - uchar mask_data = *(mask + mask_index); - - short4 src_data1 = *((__global short4 *)((__global char *)src1 + src1_index)); - short4 src_data2 = *((__global short4 *)((__global char *)src2 + src2_index)); - short4 dst_data = *((__global short4 *)((__global char *)dst + dst_index)); - - short4 data = convert_short4_sat(ARITHM_OP(convert_int4_sat(src_data1), convert_int4_sat(src_data2))); - data = mask_data ? data : dst_data; + int src1_index = mad24(y, src1_step, x + src1_offset); + int src2_index = mad24(y, src2_step, x + src2_offset); + int dst_index = mad24(y, dst_step, x + dst_offset); - *((__global short4 *)((__global char *)dst + dst_index)) = data; + WT value = convertToWT(src1[src1_index]) - convertToWT(src2[src2_index]); + value = value > (WT)(0) ? value : -value; + dst[dst_index] = convertToT(value); } } -__kernel void arithm_add_with_mask_C4_D4 (__global int *src1, int src1_step, int src1_offset, - __global int *src2, int src2_step, int src2_offset, - __global uchar *mask, int mask_step, int mask_offset, - __global int *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 4) + src1_offset); - int src2_index = mad24(y, src2_step, (x << 4) + src2_offset); - int mask_index = mad24(y, mask_step, x + mask_offset); - int dst_index = mad24(y, dst_step, (x << 4) + dst_offset); - - uchar mask_data = *(mask + mask_index); - - int4 src_data1 = *((__global int4 *)((__global char *)src1 + src1_index)); - int4 src_data2 = *((__global int4 *)((__global char *)src2 + src2_index)); - int4 dst_data = *((__global int4 *)((__global char *)dst + dst_index)); - - int4 data = convert_int4_sat(ARITHM_OP(convert_long4_sat(src_data1), convert_long4_sat(src_data2))); - data = mask_data ? data : dst_data; - - *((__global int4 *)((__global char *)dst + dst_index)) = data; - } -} -__kernel void arithm_add_with_mask_C4_D5 (__global float *src1, int src1_step, int src1_offset, - __global float *src2, int src2_step, int src2_offset, - __global uchar *mask, int mask_step, int mask_offset, - __global float *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) +// add mat with scale for multiply +__kernel void arithm_binary_op_mat_scalar(__global T *src1, int src1_step, int src1_offset, + __global T *src2, int src2_step, int src2_offset, + __global WT *scalar, + __global T *dst, int dst_step, int dst_offset, + int cols, int rows) { - int x = get_global_id(0); int y = get_global_id(1); if (x < cols && y < rows) { - int src1_index = mad24(y, src1_step, (x << 4) + src1_offset); - int src2_index = mad24(y, src2_step, (x << 4) + src2_offset); - int mask_index = mad24(y, mask_step, x + mask_offset); - int dst_index = mad24(y, dst_step, (x << 4) + dst_offset); + int src1_index = mad24(y, src1_step, x + src1_offset); + int src2_index = mad24(y, src2_step, x + src2_offset); + int dst_index = mad24(y, dst_step, x + dst_offset); - uchar mask_data = *(mask + mask_index); - - float4 src_data1 = *((__global float4 *)((__global char *)src1 + src1_index)); - float4 src_data2 = *((__global float4 *)((__global char *)src2 + src2_index)); - float4 dst_data = *((__global float4 *)((__global char *)dst + dst_index)); - - float4 data = ARITHM_OP(src_data1, src_data2); - data = mask_data ? data : dst_data; - - *((__global float4 *)((__global char *)dst + dst_index)) = data; + dst[dst_index] = convertToT(convertToWT(src1[src1_index]) * scalar[0] * convertToWT(src2[src2_index])); } } -#if defined (DOUBLE_SUPPORT) -__kernel void arithm_add_with_mask_C4_D6 (__global double *src1, int src1_step, int src1_offset, - __global double *src2, int src2_step, int src2_offset, - __global uchar *mask, int mask_step, int mask_offset, - __global double *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) +// add mat with scale for divide +__kernel void arithm_binary_op_mat_scalar_div(__global T *src1, int src1_step, int src1_offset, + __global T *src2, int src2_step, int src2_offset, + __global WT *scalar, + __global T *dst, int dst_step, int dst_offset, + int cols, int rows) { - int x = get_global_id(0); int y = get_global_id(1); if (x < cols && y < rows) { - int src1_index = mad24(y, src1_step, (x << 5) + src1_offset); - int src2_index = mad24(y, src2_step, (x << 5) + src2_offset); - int mask_index = mad24(y, mask_step, x + mask_offset); - int dst_index = mad24(y, dst_step, (x << 5) + dst_offset); + int src1_index = mad24(y, src1_step, x + src1_offset); + int src2_index = mad24(y, src2_step, x + src2_offset); + int dst_index = mad24(y, dst_step, x + dst_offset); - uchar mask_data = *(mask + mask_index); - - double4 src_data1 = *((__global double4 *)((__global char *)src1 + src1_index)); - double4 src_data2 = *((__global double4 *)((__global char *)src2 + src2_index)); - double4 dst_data = *((__global double4 *)((__global char *)dst + dst_index)); - - double4 data = ARITHM_OP(src_data1, src_data2); - data = mask_data ? data : dst_data; - - *((__global double4 *)((__global char *)dst + dst_index)) = data; + T zero = (T)(0); + dst[dst_index] = src2[src2_index] == zero ? zero : + convertToT(convertToWT(src1[src1_index]) * scalar[0] / convertToWT(src2[src2_index])); } } -#endif diff --git a/modules/ocl/src/opencl/arithm_add_mask.cl b/modules/ocl/src/opencl/arithm_add_mask.cl new file mode 100644 index 0000000000..52dbfc455c --- /dev/null +++ b/modules/ocl/src/opencl/arithm_add_mask.cl @@ -0,0 +1,79 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2010-2012, Institute Of Software Chinese Academy Of Science, all rights reserved. +// Copyright (C) 2010-2012, Advanced Micro Devices, Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// @Authors +// Jia Haipeng, jiahaipeng95@gmail.com +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other oclMaterials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors as is and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#if defined (DOUBLE_SUPPORT) +#ifdef cl_khr_fp64 +#pragma OPENCL EXTENSION cl_khr_fp64:enable +#elif defined (cl_amd_fp64) +#pragma OPENCL EXTENSION cl_amd_fp64:enable +#endif +#endif + +////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////// add with mask ////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////// + +__kernel void arithm_binary_op_mat_mask(__global T * src1, int src1_step, int src1_offset, + __global T * src2, int src2_step, int src2_offset, + __global uchar * mask, int mask_step, int mask_offset, + __global T * dst, int dst_step, int dst_offset, + int cols, int rows) +{ + int x = get_global_id(0); + int y = get_global_id(1); + + if (x < cols && y < rows) + { + int mask_index = mad24(y, mask_step, x + mask_offset); + if (mask[mask_index]) + { + int src1_index = mad24(y, src1_step, x + src1_offset); + int src2_index = mad24(y, src2_step, x + src2_offset); + int dst_index = mad24(y, dst_step, dst_offset + x); + + dst[dst_index] = convertToT(convertToWT(src1[src1_index]) Operation convertToWT(src2[src2_index])); + } + } +} diff --git a/modules/ocl/src/opencl/arithm_add_scalar.cl b/modules/ocl/src/opencl/arithm_add_scalar.cl index cdb79f37ed..4e0c7fc5fa 100644 --- a/modules/ocl/src/opencl/arithm_add_scalar.cl +++ b/modules/ocl/src/opencl/arithm_add_scalar.cl @@ -51,463 +51,61 @@ #endif #endif -#ifdef ARITHM_ADD - #define ARITHM_OP(A,B) ((A)+(B)) -#elif defined ARITHM_SUB - #define ARITHM_OP(A,B) ((A)-(B)) -#endif -/**************************************add with scalar without mask**************************************/ -__kernel void arithm_s_add_C1_D0 (__global uchar *src1, int src1_step, int src1_offset, - __global uchar *dst, int dst_step, int dst_offset, - int4 src2, int rows, int cols, int dst_step1) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 2; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align (dst_offset & 3) - int src1_index = mad24(y, src1_step, x + src1_offset - dst_align); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + x & (int)0xfffffffc); - int src1_index_fix = src1_index < 0 ? 0 : src1_index; - uchar4 src1_data = vload4(0, src1 + src1_index_fix); - int4 src2_data = (int4)(src2.x, src2.x, src2.x, src2.x); - if(src1_index < 0) - { - uchar4 tmp; - tmp.xyzw = (src1_index == -2) ? src1_data.zwxy:src1_data.yzwx; - src1_data.xyzw = (src1_index == -1) ? src1_data.wxyz:tmp.xyzw; - } - - uchar4 data = *((__global uchar4 *)(dst + dst_index)); - int4 tmp = ARITHM_OP(convert_int4_sat(src1_data), src2_data); - uchar4 tmp_data = convert_uchar4_sat(tmp); - - data.x = ((dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : data.x; - data.y = ((dst_index + 1 >= dst_start) && (dst_index + 1 < dst_end)) ? tmp_data.y : data.y; - data.z = ((dst_index + 2 >= dst_start) && (dst_index + 2 < dst_end)) ? tmp_data.z : data.z; - data.w = ((dst_index + 3 >= dst_start) && (dst_index + 3 < dst_end)) ? tmp_data.w : data.w; - - *((__global uchar4 *)(dst + dst_index)) = data; - } -} -__kernel void arithm_s_add_C1_D2 (__global ushort *src1, int src1_step, int src1_offset, - __global ushort *dst, int dst_step, int dst_offset, - int4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 1; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align ((dst_offset >> 1) & 1) - int src1_index = mad24(y, src1_step, (x << 1) + src1_offset - (dst_align << 1)); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + (x << 1) & (int)0xfffffffc); - - ushort2 src1_data = vload2(0, (__global ushort *)((__global char *)src1 + src1_index)); - int2 src2_data = (int2)(src2.x, src2.x); - - ushort2 data = *((__global ushort2 *)((__global uchar *)dst + dst_index)); - int2 tmp = ARITHM_OP(convert_int2_sat(src1_data), src2_data); - ushort2 tmp_data = convert_ushort2_sat(tmp); - - data.x = (dst_index + 0 >= dst_start) ? tmp_data.x : data.x; - data.y = (dst_index + 2 < dst_end ) ? tmp_data.y : data.y; - - *((__global ushort2 *)((__global uchar *)dst + dst_index)) = data; - } -} -__kernel void arithm_s_add_C1_D3 (__global short *src1, int src1_step, int src1_offset, - __global short *dst, int dst_step, int dst_offset, - int4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 1; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align ((dst_offset >> 1) & 1) - int src1_index = mad24(y, src1_step, (x << 1) + src1_offset - (dst_align << 1)); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + (x << 1) & (int)0xfffffffc); - - short2 src1_data = vload2(0, (__global short *)((__global char *)src1 + src1_index)); - int2 src2_data = (int2)(src2.x, src2.x); - short2 data = *((__global short2 *)((__global uchar *)dst + dst_index)); - - int2 tmp = ARITHM_OP(convert_int2_sat(src1_data), src2_data); - short2 tmp_data = convert_short2_sat(tmp); - - data.x = (dst_index + 0 >= dst_start) ? tmp_data.x : data.x; - data.y = (dst_index + 2 < dst_end ) ? tmp_data.y : data.y; - - *((__global short2 *)((__global uchar *)dst + dst_index)) = data; - } -} -__kernel void arithm_s_add_C1_D4 (__global int *src1, int src1_step, int src1_offset, - __global int *dst, int dst_step, int dst_offset, - int4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 2) + src1_offset); - int dst_index = mad24(y, dst_step, (x << 2) + dst_offset); - - int src_data1 = *((__global int *)((__global char *)src1 + src1_index)); - int src_data2 = src2.x; - int dst_data = *((__global int *)((__global char *)dst + dst_index)); - - int data = convert_int_sat(ARITHM_OP((long)src_data1, (long)src_data2)); - - *((__global int *)((__global char *)dst + dst_index)) = data; - } -} -__kernel void arithm_s_add_C1_D5 (__global float *src1, int src1_step, int src1_offset, - __global float *dst, int dst_step, int dst_offset, - float4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 2) + src1_offset); - int dst_index = mad24(y, dst_step, (x << 2) + dst_offset); - - float src_data1 = *((__global float *)((__global char *)src1 + src1_index)); - float src_data2 = src2.x; - float dst_data = *((__global float *)((__global char *)dst + dst_index)); - - float data = ARITHM_OP(src_data1, src_data2); - - *((__global float *)((__global char *)dst + dst_index)) = data; - } -} - -#if defined (DOUBLE_SUPPORT) -__kernel void arithm_s_add_C1_D6 (__global double *src1, int src1_step, int src1_offset, - __global double *dst, int dst_step, int dst_offset, - double4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 3) + src1_offset); - int dst_index = mad24(y, dst_step, (x << 3) + dst_offset); - - double src_data1 = *((__global double *)((__global char *)src1 + src1_index)); - double src2_data = src2.x; - double dst_data = *((__global double *)((__global char *)dst + dst_index)); - - double data = ARITHM_OP(src_data1, src2_data); - - *((__global double *)((__global char *)dst + dst_index)) = data; - } -} -#endif - -__kernel void arithm_s_add_C2_D0 (__global uchar *src1, int src1_step, int src1_offset, - __global uchar *dst, int dst_step, int dst_offset, - int4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 1; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align ((dst_offset >> 1) & 1) - int src1_index = mad24(y, src1_step, (x << 1) + src1_offset - (dst_align << 1)); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + (x << 1) & (int)0xfffffffc); - - uchar4 src1_data = vload4(0, src1 + src1_index); - int4 src2_data = (int4)(src2.x, src2.y, src2.x, src2.y); - - uchar4 data = *((__global uchar4 *)(dst + dst_index)); - int4 tmp = ARITHM_OP(convert_int4_sat(src1_data), src2_data); - uchar4 tmp_data = convert_uchar4_sat(tmp); - - data.xy = (dst_index + 0 >= dst_start) ? tmp_data.xy : data.xy; - data.zw = (dst_index + 2 < dst_end ) ? tmp_data.zw : data.zw; - - *((__global uchar4 *)(dst + dst_index)) = data; - } -} -__kernel void arithm_s_add_C2_D2 (__global ushort *src1, int src1_step, int src1_offset, - __global ushort *dst, int dst_step, int dst_offset, - int4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 2) + src1_offset); - int dst_index = mad24(y, dst_step, (x << 2) + dst_offset); - - ushort2 src_data1 = *((__global ushort2 *)((__global char *)src1 + src1_index)); - int2 src_data2 = (int2)(src2.x, src2.y); - ushort2 dst_data = *((__global ushort2 *)((__global char *)dst + dst_index)); - - int2 tmp = ARITHM_OP(convert_int2_sat(src_data1), src_data2); - ushort2 data = convert_ushort2_sat(tmp); +/////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////// Add with scalar ///////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////// - *((__global ushort2 *)((__global char *)dst + dst_index)) = data; - } -} -__kernel void arithm_s_add_C2_D3 (__global short *src1, int src1_step, int src1_offset, - __global short *dst, int dst_step, int dst_offset, - int4 src2, int rows, int cols, int dst_step1) +__kernel void arithm_binary_op_scalar (__global T *src1, int src1_step, int src1_offset, + __global WT *scalar, + __global T *dst, int dst_step, int dst_offset, + int cols, int rows) { - int x = get_global_id(0); int y = get_global_id(1); if (x < cols && y < rows) { - int src1_index = mad24(y, src1_step, (x << 2) + src1_offset); - int dst_index = mad24(y, dst_step, (x << 2) + dst_offset); - - short2 src_data1 = *((__global short2 *)((__global char *)src1 + src1_index)); - int2 src_data2 = (int2)(src2.x, src2.y); - short2 dst_data = *((__global short2 *)((__global char *)dst + dst_index)); + int src1_index = mad24(y, src1_step, x + src1_offset); + int dst_index = mad24(y, dst_step, x + dst_offset); - int2 tmp = ARITHM_OP(convert_int2_sat(src_data1), src_data2); - short2 data = convert_short2_sat(tmp); - - *((__global short2 *)((__global char *)dst + dst_index)) = data; + dst[dst_index] = convertToT(convertToWT(src1[src1_index]) Operation scalar[0]); } } -__kernel void arithm_s_add_C2_D4 (__global int *src1, int src1_step, int src1_offset, - __global int *dst, int dst_step, int dst_offset, - int4 src2, int rows, int cols, int dst_step1) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 3) + src1_offset); - int dst_index = mad24(y, dst_step, (x << 3) + dst_offset); - - int2 src_data1 = *((__global int2 *)((__global char *)src1 + src1_index)); - int2 src_data2 = (int2)(src2.x, src2.y); - int2 dst_data = *((__global int2 *)((__global char *)dst + dst_index)); - - int2 data = convert_int2_sat(ARITHM_OP(convert_long2_sat(src_data1), convert_long2_sat(src_data2))); - *((__global int2 *)((__global char *)dst + dst_index)) = data; - } -} -__kernel void arithm_s_add_C2_D5 (__global float *src1, int src1_step, int src1_offset, - __global float *dst, int dst_step, int dst_offset, - float4 src2, int rows, int cols, int dst_step1) +__kernel void arithm_absdiff_scalar(__global T *src1, int src1_step, int src1_offset, + __global WT *src2, + __global T *dst, int dst_step, int dst_offset, + int cols, int rows) { - int x = get_global_id(0); int y = get_global_id(1); if (x < cols && y < rows) { - int src1_index = mad24(y, src1_step, (x << 3) + src1_offset); - int dst_index = mad24(y, dst_step, (x << 3) + dst_offset); - - float2 src_data1 = *((__global float2 *)((__global char *)src1 + src1_index)); - float2 src_data2 = (float2)(src2.x, src2.y); - float2 dst_data = *((__global float2 *)((__global char *)dst + dst_index)); + int src1_index = mad24(y, src1_step, x + src1_offset); + int dst_index = mad24(y, dst_step, x + dst_offset); - float2 data = ARITHM_OP(src_data1, src_data2); - *((__global float2 *)((__global char *)dst + dst_index)) = data; + WT value = convertToWT(src1[src1_index]) - src2[0]; + value = value > (WT)(0) ? value : -value; + dst[dst_index] = convertToT(value); } } -#if defined (DOUBLE_SUPPORT) -__kernel void arithm_s_add_C2_D6 (__global double *src1, int src1_step, int src1_offset, - __global double *dst, int dst_step, int dst_offset, - double4 src2, int rows, int cols, int dst_step1) +// scalar divide to matrix +__kernel void arithm_binary_op_scalar_div(__global T *src1, int src1_step, int src1_offset, + __global WT *scalar, + __global T *dst, int dst_step, int dst_offset, + int cols, int rows) { - int x = get_global_id(0); int y = get_global_id(1); if (x < cols && y < rows) { - int src1_index = mad24(y, src1_step, (x << 4) + src1_offset); - int dst_index = mad24(y, dst_step, (x << 4) + dst_offset); - - double2 src_data1 = *((__global double2 *)((__global char *)src1 + src1_index)); - double2 src_data2 = (double2)(src2.x, src2.y); - double2 dst_data = *((__global double2 *)((__global char *)dst + dst_index)); + int src1_index = mad24(y, src1_step, x + src1_offset); + int dst_index = mad24(y, dst_step, x + dst_offset); - double2 data = ARITHM_OP(src_data1, src_data2); - - *((__global double2 *)((__global char *)dst + dst_index)) = data; - } -} -#endif - -__kernel void arithm_s_add_C4_D0 (__global uchar *src1, int src1_step, int src1_offset, - __global uchar *dst, int dst_step, int dst_offset, - int4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 2) + src1_offset); - int dst_index = mad24(y, dst_step, (x << 2) + dst_offset); - - uchar4 src_data1 = *((__global uchar4 *)(src1 + src1_index)); - - uchar4 data = convert_uchar4_sat(ARITHM_OP(convert_int4_sat(src_data1), src2)); - - *((__global uchar4 *)(dst + dst_index)) = data; + T zero = (T)(0); + dst[dst_index] = src1[src1_index] == zero ? zero : convertToT(scalar[0] / convertToWT(src1[src1_index])); } } -__kernel void arithm_s_add_C4_D2 (__global ushort *src1, int src1_step, int src1_offset, - __global ushort *dst, int dst_step, int dst_offset, - int4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 3) + src1_offset); - int dst_index = mad24(y, dst_step, (x << 3) + dst_offset); - - ushort4 src_data1 = *((__global ushort4 *)((__global char *)src1 + src1_index)); - - ushort4 data = convert_ushort4_sat(ARITHM_OP(convert_int4_sat(src_data1), src2)); - - *((__global ushort4 *)((__global char *)dst + dst_index)) = data; - } -} -__kernel void arithm_s_add_C4_D3 (__global short *src1, int src1_step, int src1_offset, - __global short *dst, int dst_step, int dst_offset, - int4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 3) + src1_offset); - int dst_index = mad24(y, dst_step, (x << 3) + dst_offset); - - short4 src_data1 = *((__global short4 *)((__global char *)src1 + src1_index)); - - short4 data = convert_short4_sat(ARITHM_OP(convert_int4_sat(src_data1), src2)); - - *((__global short4 *)((__global char *)dst + dst_index)) = data; - } -} -__kernel void arithm_s_add_C4_D4 (__global int *src1, int src1_step, int src1_offset, - __global int *dst, int dst_step, int dst_offset, - int4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 4) + src1_offset); - int dst_index = mad24(y, dst_step, (x << 4) + dst_offset); - - int4 src_data1 = *((__global int4 *)((__global char *)src1 + src1_index)); - - int4 data = convert_int4_sat(ARITHM_OP(convert_long4_sat(src_data1), convert_long4_sat(src2))); - - *((__global int4 *)((__global char *)dst + dst_index)) = data; - } -} -__kernel void arithm_s_add_C4_D5 (__global float *src1, int src1_step, int src1_offset, - __global float *dst, int dst_step, int dst_offset, - float4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 4) + src1_offset); - int dst_index = mad24(y, dst_step, (x << 4) + dst_offset); - - float4 src_data1 = *((__global float4 *)((__global char *)src1 + src1_index)); - - float4 data = ARITHM_OP(src_data1, src2); - - *((__global float4 *)((__global char *)dst + dst_index)) = data; - } -} - -#if defined (DOUBLE_SUPPORT) -__kernel void arithm_s_add_C4_D6 (__global double *src1, int src1_step, int src1_offset, - __global double *dst, int dst_step, int dst_offset, - double4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 5) + src1_offset); - int dst_index = mad24(y, dst_step, (x << 5) + dst_offset); - - double4 src_data1 = *((__global double4 *)((__global char *)src1 + src1_index)); - - double4 data = ARITHM_OP(src_data1, src2); - - *((__global double4 *)((__global char *)dst + dst_index)) = data; - } -} -#endif diff --git a/modules/ocl/src/opencl/arithm_add_scalar_mask.cl b/modules/ocl/src/opencl/arithm_add_scalar_mask.cl index a0cb7dacb4..5c34080346 100644 --- a/modules/ocl/src/opencl/arithm_add_scalar_mask.cl +++ b/modules/ocl/src/opencl/arithm_add_scalar_mask.cl @@ -51,561 +51,28 @@ #endif #endif -#ifdef ARITHM_ADD - #define ARITHM_OP(A,B) ((A)+(B)) -#elif defined ARITHM_SUB - #define ARITHM_OP(A,B) ((A)-(B)) -#endif -/**************************************add with scalar with mask**************************************/ -__kernel void arithm_s_add_with_mask_C1_D0 (__global uchar *src1, int src1_step, int src1_offset, - __global uchar *dst, int dst_step, int dst_offset, - __global uchar *mask, int mask_step, int mask_offset, - int4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 2; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align (dst_offset & 3) - int src1_index = mad24(y, src1_step, x + src1_offset - dst_align); - int mask_index = mad24(y, mask_step, x + mask_offset - dst_align); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + x & (int)0xfffffffc); - int src1_index_fix = src1_index < 0 ? 0 : src1_index; - int mask_index_fix = mask_index < 0 ? 0 : mask_index; - uchar4 src1_data = vload4(0, src1 + src1_index_fix); - int4 src2_data = (int4)(src2.x, src2.x, src2.x, src2.x); - uchar4 mask_data = vload4(0, mask + mask_index_fix); - if(src1_index < 0) - { - uchar4 tmp; - tmp.xyzw = (src1_index == -2) ? src1_data.zwxy:src1_data.yzwx; - src1_data.xyzw = (src1_index == -1) ? src1_data.wxyz:tmp.xyzw; - } - if(mask_index < 0) - { - uchar4 tmp; - tmp.xyzw = (mask_index == -2) ? mask_data.zwxy:mask_data.yzwx; - mask_data.xyzw = (mask_index == -1) ? mask_data.wxyz:tmp.xyzw; - } - - uchar4 data = *((__global uchar4 *)(dst + dst_index)); - int4 tmp = ARITHM_OP(convert_int4_sat(src1_data), src2_data); - uchar4 tmp_data = convert_uchar4_sat(tmp); - - data.x = ((mask_data.x) && (dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : data.x; - data.y = ((mask_data.y) && (dst_index + 1 >= dst_start) && (dst_index + 1 < dst_end)) ? tmp_data.y : data.y; - data.z = ((mask_data.z) && (dst_index + 2 >= dst_start) && (dst_index + 2 < dst_end)) ? tmp_data.z : data.z; - data.w = ((mask_data.w) && (dst_index + 3 >= dst_start) && (dst_index + 3 < dst_end)) ? tmp_data.w : data.w; - - *((__global uchar4 *)(dst + dst_index)) = data; - } -} -__kernel void arithm_s_add_with_mask_C1_D2 (__global ushort *src1, int src1_step, int src1_offset, - __global ushort *dst, int dst_step, int dst_offset, - __global uchar *mask, int mask_step, int mask_offset, - int4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 1; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align ((dst_offset / 2) & 1) - int src1_index = mad24(y, src1_step, (x << 1) + src1_offset - (dst_align << 1)); - int mask_index = mad24(y, mask_step, x + mask_offset - dst_align); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + (x << 1) & (int)0xfffffffc); - - ushort2 src1_data = vload2(0, (__global ushort *)((__global char *)src1 + src1_index)); - int2 src2_data = (int2)(src2.x, src2.x); - uchar2 mask_data = vload2(0, mask + mask_index); - - ushort2 data = *((__global ushort2 *)((__global uchar *)dst + dst_index)); - int2 tmp = ARITHM_OP(convert_int2_sat(src1_data), src2_data); - ushort2 tmp_data = convert_ushort2_sat(tmp); +/////////////////////////////////////////////////////////////////////////////////// +//////////////////////////// Add with scalar with mask //////////////////////////// +/////////////////////////////////////////////////////////////////////////////////// - data.x = ((mask_data.x) && (dst_index + 0 >= dst_start)) ? tmp_data.x : data.x; - data.y = ((mask_data.y) && (dst_index + 2 < dst_end )) ? tmp_data.y : data.y; - - *((__global ushort2 *)((__global uchar *)dst + dst_index)) = data; - } -} -__kernel void arithm_s_add_with_mask_C1_D3 (__global short *src1, int src1_step, int src1_offset, - __global short *dst, int dst_step, int dst_offset, - __global uchar *mask, int mask_step, int mask_offset, - int4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 1; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align ((dst_offset / 2) & 1) - int src1_index = mad24(y, src1_step, (x << 1) + src1_offset - (dst_align << 1)); - int mask_index = mad24(y, mask_step, x + mask_offset - dst_align); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + (x << 1) & (int)0xfffffffc); - - short2 src1_data = vload2(0, (__global short *)((__global char *)src1 + src1_index)); - int2 src2_data = (int2)(src2.x, src2.x); - uchar2 mask_data = vload2(0, mask + mask_index); - - short2 data = *((__global short2 *)((__global uchar *)dst + dst_index)); - int2 tmp = ARITHM_OP(convert_int2_sat(src1_data), src2_data); - short2 tmp_data = convert_short2_sat(tmp); - - data.x = ((mask_data.x) && (dst_index + 0 >= dst_start)) ? tmp_data.x : data.x; - data.y = ((mask_data.y) && (dst_index + 2 < dst_end )) ? tmp_data.y : data.y; - - *((__global short2 *)((__global uchar *)dst + dst_index)) = data; - } -} -__kernel void arithm_s_add_with_mask_C1_D4 (__global int *src1, int src1_step, int src1_offset, - __global int *dst, int dst_step, int dst_offset, - __global uchar *mask, int mask_step, int mask_offset, - int4 src2, int rows, int cols, int dst_step1) +__kernel void arithm_binary_op_scalar_mask(__global T *src1, int src1_step, int src1_offset, + __global WT *scalar, + __global uchar *mask, int mask_step, int mask_offset, + __global T *dst, int dst_step, int dst_offset, + int cols, int rows) { - int x = get_global_id(0); int y = get_global_id(1); if (x < cols && y < rows) { - int src1_index = mad24(y, src1_step, (x << 2) + src1_offset); - int mask_index = mad24(y, mask_step, x + mask_offset); - int dst_index = mad24(y, dst_step, (x << 2) + dst_offset); - - uchar mask_data = *(mask + mask_index); - - int src_data1 = *((__global int *)((__global char *)src1 + src1_index)); - int src_data2 = src2.x; - int dst_data = *((__global int *)((__global char *)dst + dst_index)); - - int data = convert_int_sat(ARITHM_OP((long)src_data1, (long)src_data2)); - data = mask_data ? data : dst_data; - - *((__global int *)((__global char *)dst + dst_index)) = data; - } -} - -__kernel void arithm_s_add_with_mask_C1_D5 (__global float *src1, int src1_step, int src1_offset, - __global float *dst, int dst_step, int dst_offset, - __global uchar *mask, int mask_step, int mask_offset, - float4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 2) + src1_offset); - int mask_index = mad24(y, mask_step, x + mask_offset); - int dst_index = mad24(y, dst_step, (x << 2) + dst_offset); - - uchar mask_data = *(mask + mask_index); - - float src_data1 = *((__global float *)((__global char *)src1 + src1_index)); - float src_data2 = src2.x; - float dst_data = *((__global float *)((__global char *)dst + dst_index)); - - float data = ARITHM_OP(src_data1, src_data2); - data = mask_data ? data : dst_data; - - *((__global float *)((__global char *)dst + dst_index)) = data; - } -} - - -#if defined (DOUBLE_SUPPORT) -__kernel void arithm_s_add_with_mask_C1_D6 (__global double *src1, int src1_step, int src1_offset, - __global double *dst, int dst_step, int dst_offset, - __global uchar *mask, int mask_step, int mask_offset, - double4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 3) + src1_offset); - int mask_index = mad24(y, mask_step, x + mask_offset); - int dst_index = mad24(y, dst_step, (x << 3) + dst_offset); - - uchar mask_data = *(mask + mask_index); - - double src_data1 = *((__global double *)((__global char *)src1 + src1_index)); - double src_data2 = src2.x; - double dst_data = *((__global double *)((__global char *)dst + dst_index)); - - double data = ARITHM_OP(src_data1, src_data2); - data = mask_data ? data : dst_data; - - *((__global double *)((__global char *)dst + dst_index)) = data; - } -} -#endif -__kernel void arithm_s_add_with_mask_C2_D0 (__global uchar *src1, int src1_step, int src1_offset, - __global uchar *dst, int dst_step, int dst_offset, - __global uchar *mask, int mask_step, int mask_offset, - int4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 1; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align ((dst_offset / 2) & 1) - int src1_index = mad24(y, src1_step, (x << 1) + src1_offset - (dst_align << 1)); - int mask_index = mad24(y, mask_step, x + mask_offset - dst_align); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + (x << 1) & (int)0xfffffffc); - - uchar4 src1_data = vload4(0, src1 + src1_index); - int4 src2_data = (int4)(src2.x, src2.y, src2.x, src2.y); - uchar2 mask_data = vload2(0, mask + mask_index); - - uchar4 data = *((__global uchar4 *)(dst + dst_index)); - int4 tmp = ARITHM_OP(convert_int4_sat(src1_data), src2_data); - uchar4 tmp_data = convert_uchar4_sat(tmp); - - data.xy = ((mask_data.x) && (dst_index + 0 >= dst_start)) ? tmp_data.xy : data.xy; - data.zw = ((mask_data.y) && (dst_index + 2 < dst_end )) ? tmp_data.zw : data.zw; - - *((__global uchar4 *)(dst + dst_index)) = data; - } -} -__kernel void arithm_s_add_with_mask_C2_D2 (__global ushort *src1, int src1_step, int src1_offset, - __global ushort *dst, int dst_step, int dst_offset, - __global uchar *mask, int mask_step, int mask_offset, - int4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 2) + src1_offset); - int mask_index = mad24(y, mask_step, x + mask_offset); - int dst_index = mad24(y, dst_step, (x << 2) + dst_offset); - - uchar mask_data = *(mask + mask_index); - - ushort2 src_data1 = *((__global ushort2 *)((__global char *)src1 + src1_index)); - int2 src_data2 = (int2)(src2.x, src2.y); - ushort2 dst_data = *((__global ushort2 *)((__global char *)dst + dst_index)); - - int2 tmp = ARITHM_OP(convert_int2_sat(src_data1), src_data2); - ushort2 data = convert_ushort2_sat(tmp); - data = mask_data ? data : dst_data; - - *((__global ushort2 *)((__global char *)dst + dst_index)) = data; - } -} -__kernel void arithm_s_add_with_mask_C2_D3 (__global short *src1, int src1_step, int src1_offset, - __global short *dst, int dst_step, int dst_offset, - __global uchar *mask, int mask_step, int mask_offset, - int4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 2) + src1_offset); - int mask_index = mad24(y, mask_step, x + mask_offset); - int dst_index = mad24(y, dst_step, (x << 2) + dst_offset); - - uchar mask_data = *(mask + mask_index); - - short2 src_data1 = *((__global short2 *)((__global char *)src1 + src1_index)); - int2 src_data2 = (int2)(src2.x, src2.y); - short2 dst_data = *((__global short2 *)((__global char *)dst + dst_index)); - - int2 tmp = ARITHM_OP(convert_int2_sat(src_data1), src_data2); - short2 data = convert_short2_sat(tmp); - data = mask_data ? data : dst_data; - - *((__global short2 *)((__global char *)dst + dst_index)) = data; - } -} -__kernel void arithm_s_add_with_mask_C2_D4 (__global int *src1, int src1_step, int src1_offset, - __global int *dst, int dst_step, int dst_offset, - __global uchar *mask, int mask_step, int mask_offset, - int4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 3) + src1_offset); - int mask_index = mad24(y, mask_step, x + mask_offset); - int dst_index = mad24(y, dst_step, (x << 3) + dst_offset); - - uchar mask_data = *(mask + mask_index); - - int2 src_data1 = *((__global int2 *)((__global char *)src1 + src1_index)); - int2 src_data2 = (int2)(src2.x, src2.y); - int2 dst_data = *((__global int2 *)((__global char *)dst + dst_index)); - - int2 data = convert_int2_sat(ARITHM_OP(convert_long2_sat(src_data1), convert_long2_sat(src_data2))); - data = mask_data ? data : dst_data; - - *((__global int2 *)((__global char *)dst + dst_index)) = data; - } -} -__kernel void arithm_s_add_with_mask_C2_D5 (__global float *src1, int src1_step, int src1_offset, - __global float *dst, int dst_step, int dst_offset, - __global uchar *mask, int mask_step, int mask_offset, - float4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 3) + src1_offset); - int mask_index = mad24(y, mask_step, x + mask_offset); - int dst_index = mad24(y, dst_step, (x << 3) + dst_offset); - - uchar mask_data = *(mask + mask_index); - - float2 src_data1 = *((__global float2 *)((__global char *)src1 + src1_index)); - float2 src_data2 = (float2)(src2.x, src2.y); - float2 dst_data = *((__global float2 *)((__global char *)dst + dst_index)); - - float2 data = ARITHM_OP(src_data1, src_data2); - data = mask_data ? data : dst_data; - - *((__global float2 *)((__global char *)dst + dst_index)) = data; - } -} - -#if defined (DOUBLE_SUPPORT) -__kernel void arithm_s_add_with_mask_C2_D6 (__global double *src1, int src1_step, int src1_offset, - __global double *dst, int dst_step, int dst_offset, - __global uchar *mask, int mask_step, int mask_offset, - double4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 4) + src1_offset); - int mask_index = mad24(y, mask_step, x + mask_offset); - int dst_index = mad24(y, dst_step, (x << 4) + dst_offset); - - uchar mask_data = *(mask + mask_index); - - double2 src_data1 = *((__global double2 *)((__global char *)src1 + src1_index)); - double2 src_data2 = (double2)(src2.x, src2.y); - double2 dst_data = *((__global double2 *)((__global char *)dst + dst_index)); - - double2 data = ARITHM_OP(src_data1, src_data2); - data = mask_data ? data : dst_data; - - *((__global double2 *)((__global char *)dst + dst_index)) = data; - } -} -#endif - -__kernel void arithm_s_add_with_mask_C4_D0 (__global uchar *src1, int src1_step, int src1_offset, - __global uchar *dst, int dst_step, int dst_offset, - __global uchar *mask, int mask_step, int mask_offset, - int4 src2, int rows, int cols, int dst_step1) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 2) + src1_offset); - int mask_index = mad24(y, mask_step, x + mask_offset); - int dst_index = mad24(y, dst_step, (x << 2) + dst_offset); - - uchar mask_data = *(mask + mask_index); - - uchar4 src_data1 = *((__global uchar4 *)(src1 + src1_index)); - uchar4 dst_data = *((__global uchar4 *)(dst + dst_index)); - - uchar4 data = convert_uchar4_sat(ARITHM_OP(convert_int4_sat(src_data1), src2)); - data = mask_data ? data : dst_data; - - *((__global uchar4 *)(dst + dst_index)) = data; - } -} -__kernel void arithm_s_add_with_mask_C4_D2 (__global ushort *src1, int src1_step, int src1_offset, - __global ushort *dst, int dst_step, int dst_offset, - __global uchar *mask, int mask_step, int mask_offset, - int4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 3) + src1_offset); - int mask_index = mad24(y, mask_step, x + mask_offset); - int dst_index = mad24(y, dst_step, (x << 3) + dst_offset); - - uchar mask_data = *(mask + mask_index); - - ushort4 src_data1 = *((__global ushort4 *)((__global char *)src1 + src1_index)); - ushort4 dst_data = *((__global ushort4 *)((__global char *)dst + dst_index)); - - ushort4 data = convert_ushort4_sat(ARITHM_OP(convert_int4_sat(src_data1), src2)); - data = mask_data ? data : dst_data; - - *((__global ushort4 *)((__global char *)dst + dst_index)) = data; - } -} -__kernel void arithm_s_add_with_mask_C4_D3 (__global short *src1, int src1_step, int src1_offset, - __global short *dst, int dst_step, int dst_offset, - __global uchar *mask, int mask_step, int mask_offset, - int4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 3) + src1_offset); - int mask_index = mad24(y, mask_step, x + mask_offset); - int dst_index = mad24(y, dst_step, (x << 3) + dst_offset); - - uchar mask_data = *(mask + mask_index); - - short4 src_data1 = *((__global short4 *)((__global char *)src1 + src1_index)); - short4 dst_data = *((__global short4 *)((__global char *)dst + dst_index)); - - short4 data = convert_short4_sat(ARITHM_OP(convert_int4_sat(src_data1), src2)); - data = mask_data ? data : dst_data; - - *((__global short4 *)((__global char *)dst + dst_index)) = data; - } -} -__kernel void arithm_s_add_with_mask_C4_D4 (__global int *src1, int src1_step, int src1_offset, - __global int *dst, int dst_step, int dst_offset, - __global uchar *mask, int mask_step, int mask_offset, - int4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 4) + src1_offset); - int mask_index = mad24(y, mask_step, x + mask_offset); - int dst_index = mad24(y, dst_step, (x << 4) + dst_offset); - - uchar mask_data = *(mask + mask_index); - - int4 src_data1 = *((__global int4 *)((__global char *)src1 + src1_index)); - int4 dst_data = *((__global int4 *)((__global char *)dst + dst_index)); - - int4 data = convert_int4_sat(ARITHM_OP(convert_long4_sat(src_data1), convert_long4_sat(src2))); - data = mask_data ? data : dst_data; - - *((__global int4 *)((__global char *)dst + dst_index)) = data; - } -} -__kernel void arithm_s_add_with_mask_C4_D5 (__global float *src1, int src1_step, int src1_offset, - __global float *dst, int dst_step, int dst_offset, - __global uchar *mask, int mask_step, int mask_offset, - float4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 4) + src1_offset); - int mask_index = mad24(y, mask_step, x + mask_offset); - int dst_index = mad24(y, dst_step, (x << 4) + dst_offset); - - uchar mask_data = *(mask + mask_index); - - float4 src_data1 = *((__global float4 *)((__global char *)src1 + src1_index)); - float4 dst_data = *((__global float4 *)((__global char *)dst + dst_index)); - - float4 data = ARITHM_OP(src_data1, src2); - data = mask_data ? data : dst_data; - - *((__global float4 *)((__global char *)dst + dst_index)) = data; - } -} - -#if defined (DOUBLE_SUPPORT) -__kernel void arithm_s_add_with_mask_C4_D6 (__global double *src1, int src1_step, int src1_offset, - __global double *dst, int dst_step, int dst_offset, - __global uchar *mask, int mask_step, int mask_offset, - double4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 5) + src1_offset); - int mask_index = mad24(y, mask_step, x + mask_offset); - int dst_index = mad24(y, dst_step, (x << 5) + dst_offset); - - uchar mask_data = *(mask + mask_index); - - double4 src_data1 = *((__global double4 *)((__global char *)src1 + src1_index)); - double4 dst_data = *((__global double4 *)((__global char *)dst + dst_index)); - - double4 data = ARITHM_OP(src_data1, src2); - data = mask_data ? data : dst_data; + int mask_index = mad24(y, mask_step, x + mask_offset); + if (mask[mask_index]) + { + int src1_index = mad24(y, src1_step, x + src1_offset); + int dst_index = mad24(y, dst_step, dst_offset + x); - *((__global double4 *)((__global char *)dst + dst_index)) = data; + dst[dst_index] = convertToT(convertToWT(src1[src1_index]) Operation scalar[0]); + } } } -#endif diff --git a/modules/ocl/src/opencl/arithm_div.cl b/modules/ocl/src/opencl/arithm_div.cl deleted file mode 100644 index 1dce3853ff..0000000000 --- a/modules/ocl/src/opencl/arithm_div.cl +++ /dev/null @@ -1,468 +0,0 @@ -/*M/////////////////////////////////////////////////////////////////////////////////////// -// -// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. -// -// By downloading, copying, installing or using the software you agree to this license. -// If you do not agree to this license, do not download, install, -// copy or use the software. -// -// -// License Agreement -// For Open Source Computer Vision Library -// -// Copyright (C) 2010-2012, Institute Of Software Chinese Academy Of Science, all rights reserved. -// Copyright (C) 2010-2012, Advanced Micro Devices, Inc., all rights reserved. -// Third party copyrights are property of their respective owners. -// -// @Authors -// Jia Haipeng, jiahaipeng95@gmail.com -// -// Redistribution and use in source and binary forms, with or without modification, -// are permitted provided that the following conditions are met: -// -// * Redistribution's of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// * Redistribution's in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other oclMaterials provided with the distribution. -// -// * The name of the copyright holders may not be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// This software is provided by the copyright holders and contributors as is and -// any express or implied warranties, including, but not limited to, the implied -// warranties of merchantability and fitness for a particular purpose are disclaimed. -// In no event shall the Intel Corporation or contributors be liable for any direct, -// indirect, incidental, special, exemplary, or consequential damages -// (including, but not limited to, procurement of substitute goods or services; -// loss of use, data, or profits; or business interruption) however caused -// and on any theory of liability, whether in contract, strict liability, -// or tort (including negligence or otherwise) arising in any way out of -// the use of this software, even if advised of the possibility of such damage. -// -//M*/ - -#if defined (DOUBLE_SUPPORT) -#ifdef cl_khr_fp64 -#pragma OPENCL EXTENSION cl_khr_fp64:enable -#elif defined (cl_amd_fp64) -#pragma OPENCL EXTENSION cl_amd_fp64:enable -#endif -typedef double F ; -typedef double4 F4; -#define convert_F4 convert_double4 -#define convert_F double -#else -typedef float F; -typedef float4 F4; -#define convert_F4 convert_float4 -#define convert_F float -#endif - -inline uchar round2_uchar(F v) -{ - return convert_uchar_sat(round(v)); -} - -inline ushort round2_ushort(F v) -{ - return convert_ushort_sat(round(v)); -} - -inline short round2_short(F v) -{ - return convert_short_sat(round(v)); -} - -inline int round2_int(F v) -{ - return convert_int_sat(round(v)); -} -/////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////divide/////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////// -/**********************************div*********************************************/ -__kernel void arithm_div_D0 (__global uchar *src1, int src1_step, int src1_offset, - __global uchar *src2, int src2_step, int src2_offset, - __global uchar *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1, F scalar) -{ - int2 coor = (int2)(get_global_id(0), get_global_id(1)); - - if (coor.x < cols && coor.y < rows) - { - coor.x = coor.x << 2; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align (dst_offset & 3) - int2 src_index = (int2)(mad24(coor.y, src1_step, coor.x + src1_offset - dst_align), - mad24(coor.y, src2_step, coor.x + src2_offset - dst_align)); - - int4 dst_args = (int4)(mad24(coor.y, dst_step, dst_offset), - mad24(coor.y, dst_step, dst_offset + dst_step1), - mad24(coor.y, dst_step, dst_offset + coor.x & (int)0xfffffffc), - 0); - - uchar4 src1_data = vload4(0, src1 + src_index.x); - uchar4 src2_data = vload4(0, src2 + src_index.y); - uchar4 dst_data = *((__global uchar4 *)(dst + dst_args.z)); - - F4 tmp = convert_F4(src1_data) * scalar; - uchar4 tmp_data; - tmp_data.x = ((tmp.x == 0) || (src2_data.x == 0)) ? 0 : round2_uchar(tmp.x / src2_data.x); - tmp_data.y = ((tmp.y == 0) || (src2_data.y == 0)) ? 0 : round2_uchar(tmp.y / src2_data.y); - tmp_data.z = ((tmp.z == 0) || (src2_data.z == 0)) ? 0 : round2_uchar(tmp.z / src2_data.z); - tmp_data.w = ((tmp.w == 0) || (src2_data.w == 0)) ? 0 : round2_uchar(tmp.w / src2_data.w); - - dst_data.x = ((dst_args.z + 0 >= dst_args.x) && (dst_args.z + 0 < dst_args.y)) ? tmp_data.x : dst_data.x; - dst_data.y = ((dst_args.z + 1 >= dst_args.x) && (dst_args.z + 1 < dst_args.y)) ? tmp_data.y : dst_data.y; - dst_data.z = ((dst_args.z + 2 >= dst_args.x) && (dst_args.z + 2 < dst_args.y)) ? tmp_data.z : dst_data.z; - dst_data.w = ((dst_args.z + 3 >= dst_args.x) && (dst_args.z + 3 < dst_args.y)) ? tmp_data.w : dst_data.w; - - *((__global uchar4 *)(dst + dst_args.z)) = dst_data; - } -} - -__kernel void arithm_div_D2 (__global ushort *src1, int src1_step, int src1_offset, - __global ushort *src2, int src2_step, int src2_offset, - __global ushort *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1, F scalar) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 2; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align ((dst_offset >> 1) & 3) - int src1_index = mad24(y, src1_step, (x << 1) + src1_offset - (dst_align << 1)); - int src2_index = mad24(y, src2_step, (x << 1) + src2_offset - (dst_align << 1)); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + (x << 1) & (int)0xfffffff8); - - ushort4 src1_data = vload4(0, (__global ushort *)((__global char *)src1 + src1_index)); - ushort4 src2_data = vload4(0, (__global ushort *)((__global char *)src2 + src2_index)); - ushort4 dst_data = *((__global ushort4 *)((__global char *)dst + dst_index)); - - F4 tmp = convert_F4(src1_data) * scalar; - - ushort4 tmp_data; - tmp_data.x = ((tmp.x == 0) || (src2_data.x == 0)) ? 0 : round2_ushort(tmp.x / (F)src2_data.x); - tmp_data.y = ((tmp.y == 0) || (src2_data.y == 0)) ? 0 : round2_ushort(tmp.y / (F)src2_data.y); - tmp_data.z = ((tmp.z == 0) || (src2_data.z == 0)) ? 0 : round2_ushort(tmp.z / (F)src2_data.z); - tmp_data.w = ((tmp.w == 0) || (src2_data.w == 0)) ? 0 : round2_ushort(tmp.w / (F)src2_data.w); - - dst_data.x = ((dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : dst_data.x; - dst_data.y = ((dst_index + 2 >= dst_start) && (dst_index + 2 < dst_end)) ? tmp_data.y : dst_data.y; - dst_data.z = ((dst_index + 4 >= dst_start) && (dst_index + 4 < dst_end)) ? tmp_data.z : dst_data.z; - dst_data.w = ((dst_index + 6 >= dst_start) && (dst_index + 6 < dst_end)) ? tmp_data.w : dst_data.w; - - *((__global ushort4 *)((__global char *)dst + dst_index)) = dst_data; - } -} -__kernel void arithm_div_D3 (__global short *src1, int src1_step, int src1_offset, - __global short *src2, int src2_step, int src2_offset, - __global short *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1, F scalar) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 2; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align ((dst_offset >> 1) & 3) - int src1_index = mad24(y, src1_step, (x << 1) + src1_offset - (dst_align << 1)); - int src2_index = mad24(y, src2_step, (x << 1) + src2_offset - (dst_align << 1)); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + (x << 1) & (int)0xfffffff8); - - short4 src1_data = vload4(0, (__global short *)((__global char *)src1 + src1_index)); - short4 src2_data = vload4(0, (__global short *)((__global char *)src2 + src2_index)); - short4 dst_data = *((__global short4 *)((__global char *)dst + dst_index)); - - F4 tmp = convert_F4(src1_data) * scalar; - - short4 tmp_data; - tmp_data.x = ((tmp.x == 0) || (src2_data.x == 0)) ? 0 : round2_short(tmp.x / (F)src2_data.x); - tmp_data.y = ((tmp.y == 0) || (src2_data.y == 0)) ? 0 : round2_short(tmp.y / (F)src2_data.y); - tmp_data.z = ((tmp.z == 0) || (src2_data.z == 0)) ? 0 : round2_short(tmp.z / (F)src2_data.z); - tmp_data.w = ((tmp.w == 0) || (src2_data.w == 0)) ? 0 : round2_short(tmp.w / (F)src2_data.w); - - - dst_data.x = ((dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : dst_data.x; - dst_data.y = ((dst_index + 2 >= dst_start) && (dst_index + 2 < dst_end)) ? tmp_data.y : dst_data.y; - dst_data.z = ((dst_index + 4 >= dst_start) && (dst_index + 4 < dst_end)) ? tmp_data.z : dst_data.z; - dst_data.w = ((dst_index + 6 >= dst_start) && (dst_index + 6 < dst_end)) ? tmp_data.w : dst_data.w; - - *((__global short4 *)((__global char *)dst + dst_index)) = dst_data; - } -} - -__kernel void arithm_div_D4 (__global int *src1, int src1_step, int src1_offset, - __global int *src2, int src2_step, int src2_offset, - __global int *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1, F scalar) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 2) + src1_offset); - int src2_index = mad24(y, src2_step, (x << 2) + src2_offset); - int dst_index = mad24(y, dst_step, (x << 2) + dst_offset); - - int data1 = *((__global int *)((__global char *)src1 + src1_index)); - int data2 = *((__global int *)((__global char *)src2 + src2_index)); - - F tmp = (convert_F)(data1) * scalar; - int tmp_data = (tmp == 0 || data2 == 0) ? 0 : round2_int(tmp / (convert_F)(data2)); - - *((__global int *)((__global char *)dst + dst_index)) =tmp_data; - } -} - -__kernel void arithm_div_D5 (__global float *src1, int src1_step, int src1_offset, - __global float *src2, int src2_step, int src2_offset, - __global float *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1, F scalar) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 2) + src1_offset); - int src2_index = mad24(y, src2_step, (x << 2) + src2_offset); - int dst_index = mad24(y, dst_step, (x << 2) + dst_offset); - - float data1 = *((__global float *)((__global char *)src1 + src1_index)); - float data2 = *((__global float *)((__global char *)src2 + src2_index)); - - F tmp = (convert_F)(data1) * scalar; - float tmp_data = (tmp == 0 || data2 == 0) ? 0 : convert_float(tmp / (convert_F)(data2)); - - *((__global float *)((__global char *)dst + dst_index)) = tmp_data; - } -} - -#if defined (DOUBLE_SUPPORT) -__kernel void arithm_div_D6 (__global double *src1, int src1_step, int src1_offset, - __global double *src2, int src2_step, int src2_offset, - __global double *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1, double scalar) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 3) + src1_offset); - int src2_index = mad24(y, src2_step, (x << 3) + src2_offset); - int dst_index = mad24(y, dst_step, (x << 3) + dst_offset); - - double data1 = *((__global double *)((__global char *)src1 + src1_index)); - double data2 = *((__global double *)((__global char *)src2 + src2_index)); - - double tmp = data1 * scalar; - double tmp_data = (tmp == 0 || data2 == 0) ? 0 : (tmp / data2); - - *((__global double *)((__global char *)dst + dst_index)) = tmp_data; - } -} -#endif -/************************************div with scalar************************************/ -__kernel void arithm_s_div_D0 (__global uchar *src, int src_step, int src_offset, - __global uchar *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1, F scalar) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 2; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align (dst_offset & 3) - int src_index = mad24(y, src_step, x + src_offset - dst_align); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + x & (int)0xfffffffc); - - uchar4 src_data = vload4(0, src + src_index); - uchar4 dst_data = *((__global uchar4 *)(dst + dst_index)); - - uchar4 tmp_data; - tmp_data.x = ((scalar == 0) || (src_data.x == 0)) ? 0 : round2_uchar(scalar / (F)src_data.x); - tmp_data.y = ((scalar == 0) || (src_data.y == 0)) ? 0 : round2_uchar(scalar / (F)src_data.y); - tmp_data.z = ((scalar == 0) || (src_data.z == 0)) ? 0 : round2_uchar(scalar / (F)src_data.z); - tmp_data.w = ((scalar == 0) || (src_data.w == 0)) ? 0 : round2_uchar(scalar / (F)src_data.w); - - dst_data.x = ((dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : dst_data.x; - dst_data.y = ((dst_index + 1 >= dst_start) && (dst_index + 1 < dst_end)) ? tmp_data.y : dst_data.y; - dst_data.z = ((dst_index + 2 >= dst_start) && (dst_index + 2 < dst_end)) ? tmp_data.z : dst_data.z; - dst_data.w = ((dst_index + 3 >= dst_start) && (dst_index + 3 < dst_end)) ? tmp_data.w : dst_data.w; - - *((__global uchar4 *)(dst + dst_index)) = dst_data; - } -} - -__kernel void arithm_s_div_D2 (__global ushort *src, int src_step, int src_offset, - __global ushort *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1, F scalar) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 2; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align ((dst_offset >> 1) & 3) - int src_index = mad24(y, src_step, (x << 1) + src_offset - (dst_align << 1)); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + (x << 1) & (int)0xfffffff8); - - ushort4 src_data = vload4(0, (__global ushort *)((__global char *)src + src_index)); - ushort4 dst_data = *((__global ushort4 *)((__global char *)dst + dst_index)); - - ushort4 tmp_data; - tmp_data.x = ((scalar == 0) || (src_data.x == 0)) ? 0 : round2_ushort(scalar / (F)src_data.x); - tmp_data.y = ((scalar == 0) || (src_data.y == 0)) ? 0 : round2_ushort(scalar / (F)src_data.y); - tmp_data.z = ((scalar == 0) || (src_data.z == 0)) ? 0 : round2_ushort(scalar / (F)src_data.z); - tmp_data.w = ((scalar == 0) || (src_data.w == 0)) ? 0 : round2_ushort(scalar / (F)src_data.w); - - dst_data.x = ((dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : dst_data.x; - dst_data.y = ((dst_index + 2 >= dst_start) && (dst_index + 2 < dst_end)) ? tmp_data.y : dst_data.y; - dst_data.z = ((dst_index + 4 >= dst_start) && (dst_index + 4 < dst_end)) ? tmp_data.z : dst_data.z; - dst_data.w = ((dst_index + 6 >= dst_start) && (dst_index + 6 < dst_end)) ? tmp_data.w : dst_data.w; - - *((__global ushort4 *)((__global char *)dst + dst_index)) = dst_data; - } -} -__kernel void arithm_s_div_D3 (__global short *src, int src_step, int src_offset, - __global short *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1, F scalar) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 2; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align ((dst_offset >> 1) & 3) - int src_index = mad24(y, src_step, (x << 1) + src_offset - (dst_align << 1)); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + (x << 1) & (int)0xfffffff8); - - short4 src_data = vload4(0, (__global short *)((__global char *)src + src_index)); - short4 dst_data = *((__global short4 *)((__global char *)dst + dst_index)); - - short4 tmp_data; - tmp_data.x = ((scalar == 0) || (src_data.x == 0)) ? 0 : round2_short(scalar / (F)src_data.x); - tmp_data.y = ((scalar == 0) || (src_data.y == 0)) ? 0 : round2_short(scalar / (F)src_data.y); - tmp_data.z = ((scalar == 0) || (src_data.z == 0)) ? 0 : round2_short(scalar / (F)src_data.z); - tmp_data.w = ((scalar == 0) || (src_data.w == 0)) ? 0 : round2_short(scalar / (F)src_data.w); - - - dst_data.x = ((dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : dst_data.x; - dst_data.y = ((dst_index + 2 >= dst_start) && (dst_index + 2 < dst_end)) ? tmp_data.y : dst_data.y; - dst_data.z = ((dst_index + 4 >= dst_start) && (dst_index + 4 < dst_end)) ? tmp_data.z : dst_data.z; - dst_data.w = ((dst_index + 6 >= dst_start) && (dst_index + 6 < dst_end)) ? tmp_data.w : dst_data.w; - - *((__global short4 *)((__global char *)dst + dst_index)) = dst_data; - } -} - -__kernel void arithm_s_div_D4 (__global int *src, int src_step, int src_offset, - __global int *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1, F scalar) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src_index = mad24(y, src_step, (x << 2) + src_offset); - int dst_index = mad24(y, dst_step, (x << 2) + dst_offset); - - int data = *((__global int *)((__global char *)src + src_index)); - - int tmp_data = (scalar == 0 || data == 0) ? 0 : round2_int(scalar / (convert_F)(data)); - - *((__global int *)((__global char *)dst + dst_index)) =tmp_data; - } -} - -__kernel void arithm_s_div_D5 (__global float *src, int src_step, int src_offset, - __global float *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1, F scalar) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src_index = mad24(y, src_step, (x << 2) + src_offset); - int dst_index = mad24(y, dst_step, (x << 2) + dst_offset); - - float data = *((__global float *)((__global char *)src + src_index)); - - float tmp_data = (scalar == 0 || data == 0) ? 0 : convert_float(scalar / (convert_F)(data)); - - *((__global float *)((__global char *)dst + dst_index)) = tmp_data; - } -} - -#if defined (DOUBLE_SUPPORT) -__kernel void arithm_s_div_D6 (__global double *src, int src_step, int src_offset, - __global double *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1, double scalar) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src_index = mad24(y, src_step, (x << 3) + src_offset); - int dst_index = mad24(y, dst_step, (x << 3) + dst_offset); - - double data = *((__global double *)((__global char *)src + src_index)); - - double tmp_data = (scalar == 0 || data == 0) ? 0 : (scalar / data); - - *((__global double *)((__global char *)dst + dst_index)) = tmp_data; - } -} -#endif diff --git a/modules/ocl/src/opencl/arithm_mul.cl b/modules/ocl/src/opencl/arithm_mul.cl deleted file mode 100644 index bfbb5942e0..0000000000 --- a/modules/ocl/src/opencl/arithm_mul.cl +++ /dev/null @@ -1,303 +0,0 @@ -/*M/////////////////////////////////////////////////////////////////////////////////////// -// -// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. -// -// By downloading, copying, installing or using the software you agree to this license. -// If you do not agree to this license, do not download, install, -// copy or use the software. -// -// -// License Agreement -// For Open Source Computer Vision Library -// -// Copyright (C) 2010-2012, Institute Of Software Chinese Academy Of Science, all rights reserved. -// Copyright (C) 2010-2012, Advanced Micro Devices, Inc., all rights reserved. -// Third party copyrights are property of their respective owners. -// -// @Authors -// Jia Haipeng, jiahaipeng95@gmail.com -// -// Redistribution and use in source and binary forms, with or without modification, -// are permitted provided that the following conditions are met: -// -// * Redistribution's of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// * Redistribution's in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other GpuMaterials provided with the distribution. -// -// * The name of the copyright holders may not be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// This software is provided by the copyright holders and contributors as is and -// any express or implied warranties, including, but not limited to, the implied -// warranties of merchantability and fitness for a particular purpose are disclaimed. -// In no event shall the Intel Corporation or contributors be liable for any direct, -// indirect, incidental, special, exemplary, or consequential damages -// (including, but not limited to, procurement of substitute goods or services; -// loss of use, data, or profits; or business interruption) however caused -// and on any theory of liability, whether in contract, strict liability, -// or tort (including negligence or otherwise) arising in any way out of -// the use of this software, even if advised of the possibility of such damage. -// -//M*/ - -#if defined (DOUBLE_SUPPORT) -#ifdef cl_khr_fp64 -#pragma OPENCL EXTENSION cl_khr_fp64:enable -#elif defined (cl_amd_fp64) -#pragma OPENCL EXTENSION cl_amd_fp64:enable -#endif -#endif - -int4 round_int4(float4 v) -{ - v.s0 = v.s0 + (v.s0 > 0 ? 0.5 : -0.5); - v.s1 = v.s1 + (v.s1 > 0 ? 0.5 : -0.5); - v.s2 = v.s2 + (v.s2 > 0 ? 0.5 : -0.5); - v.s3 = v.s3 + (v.s3 > 0 ? 0.5 : -0.5); - - return convert_int4_sat(v); -} -uint4 round_uint4(float4 v) -{ - v.s0 = v.s0 + (v.s0 > 0 ? 0.5 : -0.5); - v.s1 = v.s1 + (v.s1 > 0 ? 0.5 : -0.5); - v.s2 = v.s2 + (v.s2 > 0 ? 0.5 : -0.5); - v.s3 = v.s3 + (v.s3 > 0 ? 0.5 : -0.5); - - return convert_uint4_sat(v); -} -long round_int(float v) -{ - v = v + (v > 0 ? 0.5 : -0.5); - - return convert_int_sat(v); -} -////////////////////////////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////multiply////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////////////////////////////// -/**************************************add without mask**************************************/ -__kernel void arithm_mul_D0 (__global uchar *src1, int src1_step, int src1_offset, - __global uchar *src2, int src2_step, int src2_offset, - __global uchar *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1, float scalar) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 2; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align (dst_offset & 3) - int src1_index = mad24(y, src1_step, x + src1_offset - dst_align); - int src2_index = mad24(y, src2_step, x + src2_offset - dst_align); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + x & (int)0xfffffffc); - - uchar4 src1_data ,src2_data; - - src1_data.x= src1_index+0 >= 0 ? src1[src1_index+0] : 0; - src1_data.y= src1_index+1 >= 0 ? src1[src1_index+1] : 0; - src1_data.z= src1_index+2 >= 0 ? src1[src1_index+2] : 0; - src1_data.w= src1_index+3 >= 0 ? src1[src1_index+3] : 0; - - src2_data.x= src2_index+0 >= 0 ? src2[src2_index+0] : 0; - src2_data.y= src2_index+1 >= 0 ? src2[src2_index+1] : 0; - src2_data.z= src2_index+2 >= 0 ? src2[src2_index+2] : 0; - src2_data.w= src2_index+3 >= 0 ? src2[src2_index+3] : 0; - - uchar4 dst_data = *((__global uchar4 *)(dst + dst_index)); - int4 tmp = convert_int4_sat(src1_data) * convert_int4_sat(src2_data); - tmp = round_int4(convert_float4(tmp) * scalar); - uchar4 tmp_data = convert_uchar4_sat(tmp); - - dst_data.x = ((dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : dst_data.x; - dst_data.y = ((dst_index + 1 >= dst_start) && (dst_index + 1 < dst_end)) ? tmp_data.y : dst_data.y; - dst_data.z = ((dst_index + 2 >= dst_start) && (dst_index + 2 < dst_end)) ? tmp_data.z : dst_data.z; - dst_data.w = ((dst_index + 3 >= dst_start) && (dst_index + 3 < dst_end)) ? tmp_data.w : dst_data.w; - - *((__global uchar4 *)(dst + dst_index)) = dst_data; - } -} -__kernel void arithm_mul_D2 (__global ushort *src1, int src1_step, int src1_offset, - __global ushort *src2, int src2_step, int src2_offset, - __global ushort *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1, float scalar) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 2; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align ((dst_offset >> 1) & 3) - int src1_index = mad24(y, src1_step, (x << 1) + src1_offset - (dst_align << 1)); - int src2_index = mad24(y, src2_step, (x << 1) + src2_offset - (dst_align << 1)); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + (x << 1) & (int)0xfffffff8); - - ushort4 src1_data = vload4(0, (__global ushort *)((__global char *)src1 + src1_index)); - ushort4 src2_data = vload4(0, (__global ushort *)((__global char *)src2 + src2_index)); - - ushort4 dst_data = *((__global ushort4 *)((__global char *)dst + dst_index)); - uint4 tmp = convert_uint4_sat(src1_data) * convert_uint4_sat(src2_data); - tmp = round_uint4(convert_float4(tmp) * scalar); - ushort4 tmp_data = convert_ushort4_sat(tmp); - - dst_data.x = ((dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : dst_data.x; - dst_data.y = ((dst_index + 2 >= dst_start) && (dst_index + 2 < dst_end)) ? tmp_data.y : dst_data.y; - dst_data.z = ((dst_index + 4 >= dst_start) && (dst_index + 4 < dst_end)) ? tmp_data.z : dst_data.z; - dst_data.w = ((dst_index + 6 >= dst_start) && (dst_index + 6 < dst_end)) ? tmp_data.w : dst_data.w; - - *((__global ushort4 *)((__global char *)dst + dst_index)) = dst_data; - } -} -__kernel void arithm_mul_D3 (__global short *src1, int src1_step, int src1_offset, - __global short *src2, int src2_step, int src2_offset, - __global short *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1, float scalar) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 2; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align ((dst_offset >> 1) & 3) - int src1_index = mad24(y, src1_step, (x << 1) + src1_offset - (dst_align << 1)); - int src2_index = mad24(y, src2_step, (x << 1) + src2_offset - (dst_align << 1)); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + (x << 1) & (int)0xfffffff8); - - short4 src1_data = vload4(0, (__global short *)((__global char *)src1 + src1_index)); - short4 src2_data = vload4(0, (__global short *)((__global char *)src2 + src2_index)); - - short4 dst_data = *((__global short4 *)((__global char *)dst + dst_index)); - int4 tmp = convert_int4_sat(src1_data) * convert_int4_sat(src2_data); - tmp = round_int4(convert_float4(tmp) * scalar); - short4 tmp_data = convert_short4_sat(tmp); - - dst_data.x = ((dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : dst_data.x; - dst_data.y = ((dst_index + 2 >= dst_start) && (dst_index + 2 < dst_end)) ? tmp_data.y : dst_data.y; - dst_data.z = ((dst_index + 4 >= dst_start) && (dst_index + 4 < dst_end)) ? tmp_data.z : dst_data.z; - dst_data.w = ((dst_index + 6 >= dst_start) && (dst_index + 6 < dst_end)) ? tmp_data.w : dst_data.w; - - *((__global short4 *)((__global char *)dst + dst_index)) = dst_data; - } -} - -__kernel void arithm_mul_D4 (__global int *src1, int src1_step, int src1_offset, - __global int *src2, int src2_step, int src2_offset, - __global int *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1, float scalar) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 2) + src1_offset); - int src2_index = mad24(y, src2_step, (x << 2) + src2_offset); - int dst_index = mad24(y, dst_step, (x << 2) + dst_offset); - - int data1 = *((__global int *)((__global char *)src1 + src1_index)); - int data2 = *((__global int *)((__global char *)src2 + src2_index)); - int tmp = data1 * data2; - tmp = round_int((float)tmp * scalar); - - *((__global int *)((__global char *)dst + dst_index)) = convert_int_sat(tmp); - } -} -__kernel void arithm_mul_D5 (__global float *src1, int src1_step, int src1_offset, - __global float *src2, int src2_step, int src2_offset, - __global float *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1, float scalar) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 2) + src1_offset); - int src2_index = mad24(y, src2_step, (x << 2) + src2_offset); - int dst_index = mad24(y, dst_step, (x << 2) + dst_offset); - - float data1 = *((__global float *)((__global char *)src1 + src1_index)); - float data2 = *((__global float *)((__global char *)src2 + src2_index)); - float tmp = data1 * data2; - tmp = tmp * scalar; - - *((__global float *)((__global char *)dst + dst_index)) = tmp; - } -} - -#if defined (DOUBLE_SUPPORT) -__kernel void arithm_mul_D6 (__global double *src1, int src1_step, int src1_offset, - __global double *src2, int src2_step, int src2_offset, - __global double *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1, double scalar) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 3) + src1_offset); - int src2_index = mad24(y, src2_step, (x << 3) + src2_offset); - int dst_index = mad24(y, dst_step, (x << 3) + dst_offset); - - double data1 = *((__global double *)((__global char *)src1 + src1_index)); - double data2 = *((__global double *)((__global char *)src2 + src2_index)); - - double tmp = data1 * data2; - tmp = tmp * scalar; - - *((__global double *)((__global char *)dst + dst_index)) = tmp; - } -} -#endif - -#ifdef DOUBLE_SUPPORT -#define SCALAR_TYPE double -#else -#define SCALAR_TYPE float -#endif - -__kernel void arithm_muls_D5 (__global float *src1, int src1_step, int src1_offset, - __global float *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1, SCALAR_TYPE scalar) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 2) + src1_offset); - int dst_index = mad24(y, dst_step, (x << 2) + dst_offset); - - float data1 = *((__global float *)((__global char *)src1 + src1_index)); - float tmp = data1 * scalar; - - *((__global float *)((__global char *)dst + dst_index)) = tmp; - } -} From bd36e556a1ff1d9df8b12a2becec692570ef3cc0 Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Tue, 24 Sep 2013 13:39:07 +0400 Subject: [PATCH 40/68] removed ocl::magnitudeSqr --- modules/ocl/include/opencv2/ocl/ocl.hpp | 3 - modules/ocl/perf/perf_arithm.cpp | 48 ----- modules/ocl/src/arithm.cpp | 88 --------- modules/ocl/src/opencl/arithm_magnitudeSqr.cl | 177 ------------------ modules/ocl/test/test_arithm.cpp | 26 --- 5 files changed, 342 deletions(-) delete mode 100644 modules/ocl/src/opencl/arithm_magnitudeSqr.cl diff --git a/modules/ocl/include/opencv2/ocl/ocl.hpp b/modules/ocl/include/opencv2/ocl/ocl.hpp index 2bfc7db454..d46ad503e8 100644 --- a/modules/ocl/include/opencv2/ocl/ocl.hpp +++ b/modules/ocl/include/opencv2/ocl/ocl.hpp @@ -538,9 +538,6 @@ namespace cv //! computes magnitude of each (x(i), y(i)) vector // supports only CV_32F CV_64F type CV_EXPORTS void magnitude(const oclMat &x, const oclMat &y, oclMat &magnitude); - CV_EXPORTS void magnitudeSqr(const oclMat &x, const oclMat &y, oclMat &magnitude); - - CV_EXPORTS void magnitudeSqr(const oclMat &x, oclMat &magnitude); //! computes angle (angle(i)) of each (x(i), y(i)) vector // supports only CV_32F CV_64F type diff --git a/modules/ocl/perf/perf_arithm.cpp b/modules/ocl/perf/perf_arithm.cpp index 814b272f02..d718ed5519 100644 --- a/modules/ocl/perf/perf_arithm.cpp +++ b/modules/ocl/perf/perf_arithm.cpp @@ -842,54 +842,6 @@ PERF_TEST_P(PowFixture, pow, OCL_TYPICAL_MAT_SIZES) OCL_PERF_ELSE } -///////////// MagnitudeSqr//////////////////////// - -typedef TestBaseWithParam MagnitudeSqrFixture; - -PERF_TEST_P(MagnitudeSqrFixture, MagnitudeSqr, OCL_TYPICAL_MAT_SIZES) -{ - const Size srcSize = GetParam(); - - Mat src1(srcSize, CV_32FC1), src2(srcSize, CV_32FC1), - dst(srcSize, CV_32FC1); - declare.in(src1, src2, WARMUP_RNG).out(dst); - - if (RUN_OCL_IMPL) - { - ocl::oclMat oclSrc1(src1), oclSrc2(src2), oclDst(srcSize, src1.type()); - - OCL_TEST_CYCLE() cv::ocl::magnitudeSqr(oclSrc1, oclSrc2, oclDst); - - oclDst.download(dst); - - SANITY_CHECK(dst, 1e-6, ERROR_RELATIVE); - } - else if (RUN_PLAIN_IMPL) - { - ASSERT_EQ(1, src1.channels()); - - TEST_CYCLE() - { - for (int y = 0; y < srcSize.height; ++y) - { - const float * const src1Data = reinterpret_cast(src1.data + src1.step * y); - const float * const src2Data = reinterpret_cast(src2.data + src2.step * y); - float * const dstData = reinterpret_cast(dst.data + dst.step * y); - for (int x = 0; x < srcSize.width; ++x) - { - float t0 = src1Data[x] * src1Data[x]; - float t1 = src2Data[x] * src2Data[x]; - dstData[x] = t0 + t1; - } - } - } - - SANITY_CHECK(dst, 1e-6, ERROR_RELATIVE); - } - else - OCL_PERF_ELSE -} - ///////////// AddWeighted//////////////////////// typedef Size_MatType AddWeightedFixture; diff --git a/modules/ocl/src/arithm.cpp b/modules/ocl/src/arithm.cpp index 03c314c7ca..5794f13163 100644 --- a/modules/ocl/src/arithm.cpp +++ b/modules/ocl/src/arithm.cpp @@ -84,7 +84,6 @@ namespace cv extern const char *arithm_bitwise_not; extern const char *arithm_compare_eq; extern const char *arithm_compare_ne; - extern const char *arithm_magnitudeSqr; extern const char *arithm_transpose; extern const char *arithm_flip; extern const char *arithm_flip_rc; @@ -1911,93 +1910,6 @@ void cv::ocl::addWeighted(const oclMat &src1, double alpha, const oclMat &src2, openCLExecuteKernel(clCxt, &arithm_addWeighted, "addWeighted", globalThreads, localThreads, args, -1, depth); } -void cv::ocl::magnitudeSqr(const oclMat &src1, const oclMat &src2, oclMat &dst) -{ - CV_Assert(src1.type() == src2.type() && src1.size() == src2.size() && - (src1.depth() == CV_32F )); - - dst.create(src1.size(), src1.type()); - - - Context *clCxt = src1.clCxt; - int channels = dst.oclchannels(); - int depth = dst.depth(); - - - int vector_lengths[4][7] = {{4, 0, 4, 4, 4, 4, 4}, - {4, 0, 4, 4, 4, 4, 4}, - {4, 0, 4, 4, 4, 4, 4}, - {4, 0, 4, 4, 4, 4, 4} - }; - - - size_t vector_length = vector_lengths[channels - 1][depth]; - int offset_cols = (dst.offset / dst.elemSize1()) & (vector_length - 1); - int cols = divUp(dst.cols * channels + offset_cols, vector_length); - - size_t localThreads[3] = { 256, 1, 1 }; - size_t globalThreads[3] = { cols, dst.rows, 1 }; - - int dst_step1 = dst.cols * dst.elemSize(); - vector > args; - args.push_back( make_pair( sizeof(cl_mem), (void *)&src1.data )); - args.push_back( make_pair( sizeof(cl_int), (void *)&src1.step )); - args.push_back( make_pair( sizeof(cl_int), (void *)&src1.offset)); - args.push_back( make_pair( sizeof(cl_mem), (void *)&src2.data )); - args.push_back( make_pair( sizeof(cl_int), (void *)&src2.step )); - args.push_back( make_pair( sizeof(cl_int), (void *)&src2.offset)); - args.push_back( make_pair( sizeof(cl_mem), (void *)&dst.data )); - args.push_back( make_pair( sizeof(cl_int), (void *)&dst.step )); - args.push_back( make_pair( sizeof(cl_int), (void *)&dst.offset)); - args.push_back( make_pair( sizeof(cl_int), (void *)&src1.rows )); - args.push_back( make_pair( sizeof(cl_int), (void *)&cols )); - args.push_back( make_pair( sizeof(cl_int), (void *)&dst_step1 )); - - openCLExecuteKernel(clCxt, &arithm_magnitudeSqr, "magnitudeSqr", globalThreads, localThreads, args, 1, depth); -} - -void cv::ocl::magnitudeSqr(const oclMat &src1, oclMat &dst) -{ - CV_Assert (src1.depth() == CV_32F ); - CV_Assert(src1.size() == dst.size()); - - dst.create(src1.size(), CV_32FC1); - - - Context *clCxt = src1.clCxt; - int channels = dst.oclchannels(); - int depth = dst.depth(); - - - int vector_lengths[4][7] = {{4, 0, 4, 4, 4, 4, 4}, - {4, 0, 4, 4, 4, 4, 4}, - {4, 0, 4, 4, 4, 4, 4}, - {4, 0, 4, 4, 4, 4, 4} - }; - - - size_t vector_length = vector_lengths[channels - 1][depth]; - int offset_cols = (dst.offset / dst.elemSize1()) & (vector_length - 1); - int cols = divUp(dst.cols * channels + offset_cols, vector_length); - - size_t localThreads[3] = { 256, 1, 1 }; - size_t globalThreads[3] = { cols, dst.rows, 1 }; - - int dst_step1 = dst.cols * dst.elemSize(); - vector > args; - args.push_back( make_pair( sizeof(cl_mem), (void *)&src1.data )); - args.push_back( make_pair( sizeof(cl_int), (void *)&src1.step )); - args.push_back( make_pair( sizeof(cl_int), (void *)&src1.offset)); - args.push_back( make_pair( sizeof(cl_mem), (void *)&dst.data )); - args.push_back( make_pair( sizeof(cl_int), (void *)&dst.step )); - args.push_back( make_pair( sizeof(cl_int), (void *)&dst.offset)); - args.push_back( make_pair( sizeof(cl_int), (void *)&src1.rows )); - args.push_back( make_pair( sizeof(cl_int), (void *)&cols )); - args.push_back( make_pair( sizeof(cl_int), (void *)&dst_step1 )); - - openCLExecuteKernel(clCxt, &arithm_magnitudeSqr, "magnitudeSqr", globalThreads, localThreads, args, 2, depth); -} - static void arithmetic_pow_run(const oclMat &src1, double p, oclMat &dst, string kernelName, const char **kernelString) { CV_Assert(src1.cols == dst.cols && src1.rows == dst.rows); diff --git a/modules/ocl/src/opencl/arithm_magnitudeSqr.cl b/modules/ocl/src/opencl/arithm_magnitudeSqr.cl deleted file mode 100644 index 3fd697ff1f..0000000000 --- a/modules/ocl/src/opencl/arithm_magnitudeSqr.cl +++ /dev/null @@ -1,177 +0,0 @@ - -/*M/////////////////////////////////////////////////////////////////////////////////////// -// -// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. -// -// By downloading, copying, installing or using the software you agree to this license. -// If you do not agree to this license, do not download, install, -// copy or use the software. -// -// -// License Agreement -// For Open Source Computer Vision Library -// -// Copyright (C) 2010-2012, Institute Of Software Chinese Academy Of Science, all rights reserved. -// Copyright (C) 2010-2012, Advanced Micro Devices, Inc., all rights reserved. -// Third party copyrights are property of their respective owners. -// -// @Authors -// Jia Haipeng, jiahaipeng95@gmail.com -// -// Redistribution and use in source and binary forms, with or without modification, -// are permitted provided that the following conditions are met: -// -// * Redistribution's of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// * Redistribution's in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other oclMaterials provided with the distribution. -// -// * The name of the copyright holders may not be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// This software is provided by the copyright holders and contributors as is and -// any express or implied warranties, including, but not limited to, the implied -// warranties of merchantability and fitness for a particular purpose are disclaimed. -// In no event shall the Intel Corporation or contributors be liable for any direct, -// indirect, incidental, special, exemplary, or consequential damages -// (including, but not limited to, procurement of substitute goods or services; -// loss of use, data, or profits; or business interruption) however caused -// and on any theory of liability, whether in contract, strict liability, -// or tort (including negligence or otherwise) arising in any way out of -// the use of this softwareif advised of the possibility of such damage. -// -//M*/ - -#if defined (DOUBLE_SUPPORT) -#pragma OPENCL EXTENSION cl_khr_fp64:enable -#endif - -////////////////////////////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////magnitudeSqr////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////////////////////////////// -__kernel void magnitudeSqr_C1_D5 (__global float *src1,int src1_step,int src1_offset, - __global float *src2, int src2_step,int src2_offset, - __global float *dst, int dst_step,int dst_offset, - int rows, int cols,int dst_step1) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - - - { - - x = x << 2; - - #define dst_align ((dst_offset >> 2) & 3) - - int src1_index = mad24(y, src1_step, (x << 2) + src1_offset - (dst_align << 2)); - int src2_index = mad24(y, src2_step, (x << 2) + src2_offset - (dst_align << 2)); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + (x << 2) -(dst_align << 2)); - int src1_index_fix = src1_index < 0 ? 0 : src1_index; - int src2_index_fix = src2_index < 0 ? 0 : src2_index; - float4 src1_data = vload4(0, (__global float *)((__global char *)src1 + src1_index_fix)); - float4 src2_data = vload4(0, (__global float *)((__global char *)src2 + src2_index_fix)); - if(src1_index < 0) - { - float4 tmp; - tmp.xyzw = (src1_index == -2) ? src1_data.zwxy:src1_data.yzwx; - src1_data.xyzw = (src1_index == -1) ? src1_data.wxyz:tmp.xyzw; - } - if(src2_index < 0) - { - float4 tmp; - tmp.xyzw = (src2_index == -2) ? src2_data.zwxy:src2_data.yzwx; - src2_data.xyzw = (src2_index == -1) ? src2_data.wxyz:tmp.xyzw; - } - float4 dst_data = *((__global float4 *)((__global char *)dst + dst_index)); - - float4 tmp_data ; - tmp_data.x = src1_data.x * src1_data.x + src2_data.x * src2_data.x; - - tmp_data.y = src1_data.y * src1_data.y + src2_data.y * src2_data.y; - - tmp_data.z = src1_data.z * src1_data.z + src2_data.z * src2_data.z; - - tmp_data.w = src1_data.w * src1_data.w + src2_data.w * src2_data.w; - - - - - dst_data.x = ((dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : dst_data.x; - dst_data.y = ((dst_index + 4 >= dst_start) && (dst_index + 4 < dst_end)) ? tmp_data.y : dst_data.y; - dst_data.z = ((dst_index + 8 >= dst_start) && (dst_index + 8 < dst_end)) ? tmp_data.z : dst_data.z; - dst_data.w = ((dst_index + 12 >= dst_start) && (dst_index + 12 < dst_end)) ? tmp_data.w : dst_data.w; - - *((__global float4 *)((__global char *)dst + dst_index)) = dst_data; - } - -} - - -#if defined (DOUBLE_SUPPORT) - -__kernel void magnitudeSqr_C2_D5 (__global float *src1,int src1_step,int src1_offset, - __global float *dst, int dst_step,int dst_offset, - int rows, int cols,int dst_step1) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - - - { - - x = x << 2; - - #define dst_align ((dst_offset >> 2) & 3) - - int src1_index = mad24(y, src1_step, (x << 3) + src1_offset - (dst_align << 3)); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + (x << 2) -(dst_align << 2)); - int src1_index_fix = src1_index < 0 ? 0 : src1_index; - - float8 src1_data = vload8(0, (__global float *)((__global char *)src1 + src1_index_fix)); - - if(src1_index==-6) - src1_data.s01234567 = src1_data.s67012345; - if(src1_index==-4) - src1_data.s01234567 = src1_data.s45670123; - if(src1_index== -2) - src1_data.s01234567 = src1_data.s23456701; - - - - float4 dst_data = *((__global float4 *)((__global char *)dst + dst_index)); - - float4 tmp_data ; - tmp_data.x = src1_data.s0 * src1_data.s0 + src1_data.s1 * src1_data.s1; - - tmp_data.y = src1_data.s2 * src1_data.s2 + src1_data.s3 * src1_data.s3; - - tmp_data.z = src1_data.s4 * src1_data.s4 + src1_data.s5 * src1_data.s5; - - tmp_data.w = src1_data.s6 * src1_data.s6 + src1_data.s7 * src1_data.s7; - - - - - dst_data.x = ((dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : dst_data.x; - dst_data.y = ((dst_index + 4 >= dst_start) && (dst_index + 4 < dst_end)) ? tmp_data.y : dst_data.y; - dst_data.z = ((dst_index + 8 >= dst_start) && (dst_index + 8 < dst_end)) ? tmp_data.z : dst_data.z; - dst_data.w = ((dst_index + 12 >= dst_start) && (dst_index + 12 < dst_end)) ? tmp_data.w : dst_data.w; - - *((__global float4 *)((__global char *)dst + dst_index)) = dst_data; - } - -} -#endif diff --git a/modules/ocl/test/test_arithm.cpp b/modules/ocl/test/test_arithm.cpp index fa9d099990..43afd13424 100644 --- a/modules/ocl/test/test_arithm.cpp +++ b/modules/ocl/test/test_arithm.cpp @@ -1159,28 +1159,6 @@ TEST_P(Pow, Mat) } -struct MagnitudeSqr : ArithmTestBase {}; - -TEST_P(MagnitudeSqr, Mat) -{ - for(int j = 0; j < LOOP_TIMES; j++) - { - random_roi(); - for(int i = 0; i < mat1.rows; ++i) - for(int j = 0; j < mat1.cols; ++j) - { - float val1 = mat1.at(i, j); - float val2 = mat2.at(i, j); - ((float *)(dst.data))[i * dst.step / 4 + j] = val1 * val1 + val2 * val2; - } - - cv::ocl::oclMat clmat1(mat1), clmat2(mat2); - cv::ocl::magnitudeSqr(clmat1, clmat2, gdst); - Near(1); - } -} - - struct AddWeighted : ArithmTestBase {}; TEST_P(AddWeighted, Mat) @@ -1302,10 +1280,6 @@ INSTANTIATE_TEST_CASE_P(Arithm, Compare, Combine(Values(CV_8UC1, CV_32SC1, CV_32 INSTANTIATE_TEST_CASE_P(Arithm, Pow, Combine(Values(CV_32FC1, CV_32FC3, CV_32FC4), Values(false))); // Values(false) is the reserved parameter -INSTANTIATE_TEST_CASE_P(Arithm, MagnitudeSqr, Combine( - Values(CV_32FC1, CV_32FC1), - Values(false))); // Values(false) is the reserved parameter - INSTANTIATE_TEST_CASE_P(Arithm, AddWeighted, Combine( Values(CV_8UC1, CV_32SC1, CV_32FC1), Values(false))); // Values(false) is the reserved parameter From 311a7233c2b66822ab5c8367d4a4c258eb782cd7 Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Tue, 24 Sep 2013 13:43:12 +0400 Subject: [PATCH 41/68] removed comments from filtering.cpp and imgproc.cpp in ocl module --- modules/ocl/src/filtering.cpp | 81 ++--------------------------------- modules/ocl/src/imgproc.cpp | 31 +------------- 2 files changed, 5 insertions(+), 107 deletions(-) diff --git a/modules/ocl/src/filtering.cpp b/modules/ocl/src/filtering.cpp index e252d852cb..284dc61632 100644 --- a/modules/ocl/src/filtering.cpp +++ b/modules/ocl/src/filtering.cpp @@ -130,7 +130,7 @@ public: { Size src_size = src.size(); - // Delete those two clause below which exist before, However, the result is alos correct + // Delete those two clause below which exist before, However, the result is also correct // dst.create(src_size, src.type()); // dst = Scalar(0.0); @@ -394,23 +394,8 @@ public: { Filter2DEngine_GPU::apply(src, dst); - //if (iters > 1) - //{ - // Size wholesize; - // Point ofs; - // dst.locateROI(wholesize,ofs); - // int rows = dst.rows, cols = dst.cols; - // dst.adjustROI(ofs.y,-ofs.y-rows+dst.wholerows,ofs.x,-ofs.x-cols+dst.wholecols); - // dst.copyTo(morfBuf); - // dst.adjustROI(-ofs.y,ofs.y+rows-dst.wholerows,-ofs.x,ofs.x+cols-dst.wholecols); - // morfBuf.adjustROI(-ofs.y,ofs.y+rows-dst.wholerows,-ofs.x,ofs.x+cols-dst.wholecols); - // //morfBuf.create(src.size(),src.type()); - // //Filter2DEngine_GPU::apply(dst, morfBuf); - // //morfBuf.copyTo(dst); - //} for (int i = 1; i < iters; ++i) { - //dst.swap(morfBuf); Size wholesize; Point ofs; dst.locateROI(wholesize, ofs); @@ -720,24 +705,16 @@ public: virtual void apply(const oclMat &src, oclMat &dst, Rect roi = Rect(0, 0, -1, -1)) { Size src_size = src.size(); - //int src_type = src.type(); int cn = src.oclchannels(); - //dst.create(src_size, src_type); - //dst = Scalar(0.0); - //dstBuf.create(src_size, src_type); dstBuf.create(src_size.height + ksize.height - 1, src_size.width, CV_MAKETYPE(CV_32F, cn)); - //dstBuf = Scalar(0.0); normalizeROI(roi, ksize, anchor, src_size); srcROI = src(roi); dstROI = dst(roi); - //dstBufROI = dstBuf(roi); (*rowFilter)(srcROI, dstBuf); - //Mat rm(dstBufROI); - //std::cout << "rm " << rm << endl; (*columnFilter)(dstBuf, dstROI); } @@ -1324,11 +1301,8 @@ void linearColumnFilter_gpu(const oclMat &src, const oclMat &dst, oclMat mat_ker CV_Assert(src.oclchannels() == dst.oclchannels()); CV_Assert(ksize == (anchor << 1) + 1); int src_pix_per_row, dst_pix_per_row; - //int src_offset_x, src_offset_y; int dst_offset_in_pixel; src_pix_per_row = src.step / src.elemSize(); - //src_offset_x = (src.offset % src.step) / src.elemSize(); - //src_offset_y = src.offset / src.step; dst_pix_per_row = dst.step / dst.elemSize(); dst_offset_in_pixel = dst.offset / dst.elemSize(); @@ -1340,8 +1314,6 @@ void linearColumnFilter_gpu(const oclMat &src, const oclMat &dst, oclMat mat_ker args.push_back(make_pair(sizeof(cl_int), (void *)&src.wholecols)); args.push_back(make_pair(sizeof(cl_int), (void *)&src.wholerows)); args.push_back(make_pair(sizeof(cl_int), (void *)&src_pix_per_row)); - //args.push_back(make_pair(sizeof(cl_int),(void*)&src_offset_x)); - //args.push_back(make_pair(sizeof(cl_int),(void*)&src_offset_y)); args.push_back(make_pair(sizeof(cl_int), (void *)&dst_pix_per_row)); args.push_back(make_pair(sizeof(cl_int), (void *)&dst_offset_in_pixel)); args.push_back(make_pair(sizeof(cl_mem), (void *)&mat_kernel.data)); @@ -1360,23 +1332,11 @@ Ptr cv::ocl::getLinearColumnFilter_GPU(int /*bufType*/, in linearColumnFilter_gpu, linearColumnFilter_gpu }; - /* - CV_Assert(dstType == CV_8UC4 || dstType == CV_8SC4 || dstType == CV_16UC2 || - dstType == CV_16SC2 || dstType == CV_32SC1 || dstType == CV_32FC1); - CV_Assert(bufType == CV_8UC4 || bufType == CV_8SC4 || bufType == CV_16UC2 || - bufType == CV_16SC2 || bufType == CV_32SC1 || bufType == CV_32FC1); - - Mat temp(columnKernel.size(), CV_32SC1); - columnKernel.convertTo(temp, CV_32SC1); - Mat cont_krnl = temp.reshape(1, 1); - */ + Mat temp = columnKernel.reshape(1, 1); oclMat mat_kernel(temp); int ksize = temp.cols; - - //CV_Assert(ksize < 16); - normalizeAnchor(anchor, ksize); return Ptr(new GpuLinearColumnFilter(ksize, anchor, mat_kernel, @@ -1414,11 +1374,8 @@ void cv::ocl::sepFilter2D(const oclMat &src, oclMat &dst, int ddepth, const Mat } if (ddepth < 0) - { ddepth = src.depth(); - } - //CV_Assert(ddepth == src.depth()); dst.create(src.size(), CV_MAKETYPE(ddepth, src.channels())); Ptr f = createSeparableLinearFilter_GPU(src.type(), dst.type(), kernelX, kernelY, anchor, delta, bordertype); @@ -1445,19 +1402,11 @@ void cv::ocl::Sobel(const oclMat &src, oclMat &dst, int ddepth, int dx, int dy, // usually the smoothing part is the slowest to compute, // so try to scale it instead of the faster differenciating part if (dx == 0) - { kx *= scale; - } else - { ky *= scale; - } } - // Mat kx_, ky_; - //ky.convertTo(ky_,CV_32S,1<<8); - //kx.convertTo(kx_,CV_32S,1<<8); - sepFilter2D(src, dst, ddepth, kx, ky, Point(-1, -1), delta, borderType); } @@ -1471,19 +1420,11 @@ void cv::ocl::Scharr(const oclMat &src, oclMat &dst, int ddepth, int dx, int dy, // usually the smoothing part is the slowest to compute, // so try to scale it instead of the faster differenciating part if (dx == 0) - { kx *= scale; - } else - { ky *= scale; - } } - // Mat kx_, ky_; - //ky.convertTo(ky_,CV_32S,1<<8); - //kx.convertTo(kx_,CV_32S,1<<8); - sepFilter2D(src, dst, ddepth, kx, ky, Point(-1, -1), delta, bordertype); } @@ -1505,9 +1446,7 @@ void cv::ocl::Laplacian(const oclMat &src, oclMat &dst, int ddepth, int ksize, d Mat kernel(3, 3, CV_32S, (void *)K[ksize == 3]); if (scale != 1) - { kernel *= scale; - } filter2D(src, dst, ddepth, kernel, Point(-1, -1)); } @@ -1526,14 +1465,10 @@ Ptr cv::ocl::createGaussianFilter_GPU(int type, Size ksize, do // automatic detection of kernel size from sigma if (ksize.width <= 0 && sigma1 > 0) - { ksize.width = cvRound(sigma1 * (depth == CV_8U ? 3 : 4) * 2 + 1) | 1; - } if (ksize.height <= 0 && sigma2 > 0) - { ksize.height = cvRound(sigma2 * (depth == CV_8U ? 3 : 4) * 2 + 1) | 1; - } CV_Assert(ksize.width > 0 && ksize.width % 2 == 1 && ksize.height > 0 && ksize.height % 2 == 1); @@ -1544,17 +1479,10 @@ Ptr cv::ocl::createGaussianFilter_GPU(int type, Size ksize, do Mat ky; if (ksize.height == ksize.width && std::abs(sigma1 - sigma2) < DBL_EPSILON) - { ky = kx; - } else - { ky = getGaussianKernel(ksize.height, sigma2, std::max(depth, CV_32F)); - } - //Mat kx_, ky_; - //kx.convertTo(kx_,CV_32S,1<<8); - //ky.convertTo(ky_,CV_32S,1<<8); return createSeparableLinearFilter_GPU(type, type, kx, ky, Point(-1, -1), 0.0, bordertype); } @@ -1585,14 +1513,10 @@ void cv::ocl::GaussianBlur(const oclMat &src, oclMat &dst, Size ksize, double si if (bordertype != BORDER_CONSTANT) { if (src.rows == 1) - { ksize.height = 1; - } if (src.cols == 1) - { ksize.width = 1; - } } Ptr f = createGaussianFilter_GPU(src.type(), ksize, sigma1, sigma2, bordertype); @@ -1618,6 +1542,7 @@ void cv::ocl::adaptiveBilateralFilter(const oclMat& src, oclMat& dst, Size ksize { lut.at(idx++) = sigma2 / (sigma2 + x * x + y * y); } + oclMat dlut(lut); int depth = src.depth(); int cn = src.oclchannels(); diff --git a/modules/ocl/src/imgproc.cpp b/modules/ocl/src/imgproc.cpp index 2ed786fe49..7d0d941dfa 100644 --- a/modules/ocl/src/imgproc.cpp +++ b/modules/ocl/src/imgproc.cpp @@ -244,9 +244,6 @@ namespace cv kernelName = "remapNNF1Constant"; } - //int channels = dst.oclchannels(); - //int depth = dst.depth(); - //int type = src.type(); size_t blkSizeX = 16, blkSizeY = 16; size_t glbSizeX; int cols = dst.cols; @@ -499,21 +496,13 @@ namespace cv openCLExecuteKernel(clCxt, &imgproc_median, kernelName, globalThreads, localThreads, args, src.oclchannels(), src.depth()); } else - { CV_Error(CV_StsUnsupportedFormat, "Non-supported filter length"); - //string kernelName = "medianFilter"; - //args.push_back( make_pair( sizeof(cl_int),(void*)&m)); - - //openCLExecuteKernel(clCxt,&imgproc_median,kernelName,globalThreads,localThreads,args,src.oclchannels(),-1); - } - } //////////////////////////////////////////////////////////////////////// // copyMakeBorder void copyMakeBorder(const oclMat &src, oclMat &dst, int top, int bottom, int left, int right, int bordertype, const Scalar &scalar) { - //CV_Assert(src.oclchannels() != 2); CV_Assert(top >= 0 && bottom >= 0 && left >= 0 && right >= 0); if((dst.cols != dst.wholecols) || (dst.rows != dst.wholerows)) //has roi { @@ -529,10 +518,12 @@ namespace cv { CV_Assert((src.cols >= left) && (src.cols >= right) && (src.rows >= top) && (src.rows >= bottom)); } + if(bordertype == cv::BORDER_REFLECT_101) { CV_Assert((src.cols > left) && (src.cols > right) && (src.rows > top) && (src.rows > bottom)); } + dst.create(src.rows + top + bottom, src.cols + left + right, src.type()); int srcStep = src.step1() / src.oclchannels(); int dstStep = dst.step1() / dst.oclchannels(); @@ -732,19 +723,6 @@ namespace cv } openCLExecuteKernel(src.clCxt, &imgproc_copymakeboder, kernelName, globalThreads, localThreads, args, -1, -1, compile_option); - //uchar* cputemp=new uchar[32*dst.wholerows]; - ////int* cpudata=new int[this->step*this->wholerows/sizeof(int)]; - //openCLSafeCall(clEnqueueReadBuffer(src.clCxt->impl->clCmdQueue, (cl_mem)dst.data, CL_TRUE, - // 0, 32*dst.wholerows, cputemp, 0, NULL, NULL)); - //for(int i=0;isupportsFeature(Context::CL_DOUBLE)) - // { - // CV_Error( CV_GpuNotSupported, "Selected device doesn't support double, so a deviation exists.\nIf the accuracy is acceptable, the error can be ignored.\n"); - // } - dst.create( src.size(), CV_8UC4 ); if( !(criteria.type & TermCriteria::MAX_ITER) ) From b20bd470fe97f4e058d114e160c7f95969282531 Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Tue, 24 Sep 2013 13:49:38 +0400 Subject: [PATCH 42/68] refactored and extended ocl::LUT --- modules/ocl/src/arithm.cpp | 117 ++++++-------------- modules/ocl/src/opencl/arithm_LUT.cl | 155 +++++++++------------------ 2 files changed, 79 insertions(+), 193 deletions(-) diff --git a/modules/ocl/src/arithm.cpp b/modules/ocl/src/arithm.cpp index 5794f13163..66180ba4db 100644 --- a/modules/ocl/src/arithm.cpp +++ b/modules/ocl/src/arithm.cpp @@ -793,100 +793,45 @@ void cv::ocl::flip(const oclMat &src, oclMat &dst, int flipCode) ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////// LUT ////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// -static void arithmetic_lut_run(const oclMat &src1, const oclMat &src2, oclMat &dst, string kernelName) + +static void arithmetic_lut_run(const oclMat &src, const oclMat &lut, oclMat &dst, string kernelName) { - Context *clCxt = src1.clCxt; - int channels = src1.oclchannels(); - int rows = src1.rows; - int cols = src1.cols; - //int step = src1.step; - int src_step = src1.step / src1.elemSize(); - int dst_step = dst.step / dst.elemSize(); - int whole_rows = src1.wholerows; - int whole_cols = src1.wholecols; - int src_offset = src1.offset / src1.elemSize(); - int dst_offset = dst.offset / dst.elemSize(); - int lut_offset = src2.offset / src2.elemSize(); - int left_col = 0, right_col = 0; - size_t localSize[] = {16, 16, 1}; - //cl_kernel kernel = openCLGetKernelFromSource(clCxt,&arithm_LUT,kernelName); - size_t globalSize[] = {(cols + localSize[0] - 1) / localSize[0] *localSize[0], (rows + localSize[1] - 1) / localSize[1] *localSize[1], 1}; - if(channels == 1 && cols > 6) - { - left_col = 4 - (dst_offset & 3); - left_col &= 3; - dst_offset += left_col; - src_offset += left_col; - cols -= left_col; - right_col = cols & 3; - cols -= right_col; - globalSize[0] = (cols / 4 + localSize[0] - 1) / localSize[0] * localSize[0]; - } - else if(channels == 1) - { - left_col = cols; - right_col = 0; - cols = 0; - globalSize[0] = 0; - } - CV_Assert(clCxt == dst.clCxt); - CV_Assert(src1.cols == dst.cols); - CV_Assert(src1.rows == dst.rows); - CV_Assert(src1.oclchannels() == dst.oclchannels()); - // CV_Assert(src1.step == dst.step); + Context *clCxt = src.clCxt; + int sdepth = src.depth(); + int src_step1 = src.step1(), dst_step1 = dst.step1(); + int src_offset1 = src.offset / src.elemSize1(), dst_offset1 = dst.offset / dst.elemSize1(); + int lut_offset1 = lut.offset / lut.elemSize1() + (sdepth == CV_8U ? 0 : 128) * lut.channels(); + int cols1 = src.cols * src.oclchannels(); + + size_t localSize[] = { 16, 16, 1 }; + size_t globalSize[] = { lut.channels() == 1 ? cols1 : src.cols, src.rows, 1 }; + + const char * const typeMap[] = { "uchar", "char", "ushort", "short", "int", "float", "double" }; + std::string buildOptions = format("-D srcT=%s -D dstT=%s", typeMap[sdepth], typeMap[dst.depth()]); + vector > args; + args.push_back( make_pair( sizeof(cl_mem), (void *)&src.data )); + args.push_back( make_pair( sizeof(cl_mem), (void *)&lut.data )); + args.push_back( make_pair( sizeof(cl_mem), (void *)&dst.data )); + args.push_back( make_pair( sizeof(cl_int), (void *)&cols1)); + args.push_back( make_pair( sizeof(cl_int), (void *)&src.rows )); + args.push_back( make_pair( sizeof(cl_int), (void *)&src_offset1 )); + args.push_back( make_pair( sizeof(cl_int), (void *)&lut_offset1 )); + args.push_back( make_pair( sizeof(cl_int), (void *)&dst_offset1 )); + args.push_back( make_pair( sizeof(cl_int), (void *)&src_step1 )); + args.push_back( make_pair( sizeof(cl_int), (void *)&dst_step1 )); - if(globalSize[0] != 0) - { - args.push_back( make_pair( sizeof(cl_mem), (void *)&dst.data )); - args.push_back( make_pair( sizeof(cl_mem), (void *)&src1.data )); - args.push_back( make_pair( sizeof(cl_mem), (void *)&src2.data )); - args.push_back( make_pair( sizeof(cl_int), (void *)&rows )); - args.push_back( make_pair( sizeof(cl_int), (void *)&cols )); - args.push_back( make_pair( sizeof(cl_int), (void *)&channels )); - args.push_back( make_pair( sizeof(cl_int), (void *)&whole_rows )); - args.push_back( make_pair( sizeof(cl_int), (void *)&whole_cols )); - args.push_back( make_pair( sizeof(cl_int), (void *)&src_offset )); - args.push_back( make_pair( sizeof(cl_int), (void *)&dst_offset )); - args.push_back( make_pair( sizeof(cl_int), (void *)&lut_offset )); - args.push_back( make_pair( sizeof(cl_int), (void *)&src_step )); - args.push_back( make_pair( sizeof(cl_int), (void *)&dst_step )); - openCLExecuteKernel(clCxt, &arithm_LUT, kernelName, globalSize, localSize, args, src1.oclchannels(), src1.depth()); - } - if(channels == 1 && (left_col != 0 || right_col != 0)) - { - src_offset = src1.offset; - dst_offset = dst.offset; - localSize[0] = 1; - localSize[1] = 256; - globalSize[0] = left_col + right_col; - globalSize[1] = (rows + localSize[1] - 1) / localSize[1] * localSize[1]; - //kernel = openCLGetKernelFromSource(clCxt,&arithm_LUT,"LUT2"); - args.clear(); - args.push_back( make_pair( sizeof(cl_mem), (void *)&dst.data )); - args.push_back( make_pair( sizeof(cl_mem), (void *)&src1.data )); - args.push_back( make_pair( sizeof(cl_mem), (void *)&src2.data )); - args.push_back( make_pair( sizeof(cl_int), (void *)&rows )); - args.push_back( make_pair( sizeof(cl_int), (void *)&left_col )); - args.push_back( make_pair( sizeof(cl_int), (void *)&channels )); - args.push_back( make_pair( sizeof(cl_int), (void *)&whole_rows )); - args.push_back( make_pair( sizeof(cl_int), (void *)&cols )); - args.push_back( make_pair( sizeof(cl_int), (void *)&src_offset )); - args.push_back( make_pair( sizeof(cl_int), (void *)&dst_offset )); - args.push_back( make_pair( sizeof(cl_int), (void *)&lut_offset )); - args.push_back( make_pair( sizeof(cl_int), (void *)&src_step )); - args.push_back( make_pair( sizeof(cl_int), (void *)&dst_step )); - openCLExecuteKernel(clCxt, &arithm_LUT, "LUT2", globalSize, localSize, args, src1.oclchannels(), src1.depth()); - } + openCLExecuteKernel(clCxt, &arithm_LUT, kernelName, globalSize, localSize, + args, lut.oclchannels(), -1, buildOptions.c_str()); } void cv::ocl::LUT(const oclMat &src, const oclMat &lut, oclMat &dst) { - int cn = src.channels(); - CV_Assert(src.depth() == CV_8U); - CV_Assert((lut.oclchannels() == 1 || lut.oclchannels() == cn) && lut.rows == 1 && lut.cols == 256); + int cn = src.channels(), depth = src.depth(); + CV_Assert(depth == CV_8U || depth == CV_8S); + CV_Assert(lut.channels() == 1 || lut.channels() == src.channels()); + CV_Assert(lut.rows == 1 && lut.cols == 256); dst.create(src.size(), CV_MAKETYPE(lut.depth(), cn)); - //oclMat _lut(lut); string kernelName = "LUT"; arithmetic_lut_run(src, lut, dst, kernelName); } diff --git a/modules/ocl/src/opencl/arithm_LUT.cl b/modules/ocl/src/opencl/arithm_LUT.cl index 624da00084..ff21e9a315 100644 --- a/modules/ocl/src/opencl/arithm_LUT.cl +++ b/modules/ocl/src/opencl/arithm_LUT.cl @@ -38,125 +38,66 @@ #pragma OPENCL EXTENSION cl_khr_fp64:enable #endif -__kernel -void LUT_C1_D0( __global uchar *dst, - __global const uchar *src, - __constant uchar *table, - int rows, - int cols, - int channels, - int whole_rows, - int whole_cols, - int src_offset, - int dst_offset, - int lut_offset, - int src_step, - int dst_step) +__kernel void LUT_C1( __global const srcT * src, __global const dstT *lut, + __global dstT *dst, + int cols1, int rows, + int src_offset1, + int lut_offset1, + int dst_offset1, + int src_step1, int dst_step1) { - int gidx = get_global_id(0)<<2; - int gidy = get_global_id(1); - int lidx = get_local_id(0); - int lidy = get_local_id(1); + int x1 = get_global_id(0); + int y = get_global_id(1); - __local uchar l[256]; - l[(lidy<<4)+lidx] = table[(lidy<<4)+lidx+lut_offset]; - //mem_fence(CLK_LOCAL_MEM_FENCE); - - - //clamp(gidx,mask,cols-1); - gidx = gidx >= cols-4?cols-4:gidx; - gidy = gidy >= rows?rows-1:gidy; - - int src_index = src_offset + mad24(gidy,src_step,gidx); - int dst_index = dst_offset + mad24(gidy,dst_step,gidx); - uchar4 p,q; - barrier(CLK_LOCAL_MEM_FENCE); - p.x = src[src_index]; - p.y = src[src_index+1]; - p.z = src[src_index+2]; - p.w = src[src_index+3]; + if (x1 < cols1 && y < rows) + { + int src_index = mad24(y, src_step1, src_offset1 + x1); + int dst_index = mad24(y, dst_step1, dst_offset1 + x1); - q.x = l[p.x]; - q.y = l[p.y]; - q.z = l[p.z]; - q.w = l[p.w]; - *(__global uchar4*)(dst + dst_index) = q; + dst[dst_index] = lut[lut_offset1 + src[src_index]]; + } } -__kernel -void LUT2_C1_D0( __global uchar *dst, - __global const uchar *src, - __constant uchar *table, - int rows, - int precols, - int channels, - int whole_rows, - int cols, - int src_offset, - int dst_offset, - int lut_offset, - int src_step, - int dst_step) +__kernel void LUT_C2( __global const srcT * src, __global const dstT *lut, + __global dstT *dst, + int cols1, int rows, + int src_offset1, + int lut_offset1, + int dst_offset1, + int src_step1, int dst_step1) { - int gidx = get_global_id(0); - int gidy = get_global_id(1); - //int lidx = get_local_id(0); - int lidy = get_local_id(1); - - __local uchar l[256]; - l[lidy] = table[lidy+lut_offset]; - //mem_fence(CLK_LOCAL_MEM_FENCE); + int x1 = get_global_id(0) << 1; + int y = get_global_id(1); + if (x1 < cols1 && y < rows) + { + int src_index = mad24(y, src_step1, src_offset1 + x1); + int dst_index = mad24(y, dst_step1, dst_offset1 + x1); - //clamp(gidx,mask,cols-1); - gidx = gidx >= precols ? cols+gidx : gidx; - gidy = gidy >= rows?rows-1:gidy; - - int src_index = src_offset + mad24(gidy,src_step,gidx); - int dst_index = dst_offset + mad24(gidy,dst_step,gidx); - //uchar4 p,q; - barrier(CLK_LOCAL_MEM_FENCE); - uchar p = src[src_index]; - uchar q = l[p]; - dst[dst_index] = q; + dst[dst_index ] = lut[lut_offset1 + (src[src_index ] << 1) ]; + dst[dst_index + 1] = x1 + 1 < cols1 ? lut[lut_offset1 + (src[src_index + 1] << 1) + 1] : dst[dst_index + 1]; + } } -__kernel -void LUT_C4_D0( __global uchar4 *dst, - __global uchar4 *src, - __constant uchar *table, - int rows, - int cols, - int channels, - int whole_rows, - int whole_cols, - int src_offset, - int dst_offset, - int lut_offset, - int src_step, - int dst_step) +__kernel void LUT_C4( __global const srcT * src, __global const dstT *lut, + __global dstT *dst, + int cols1, int rows, + int src_offset1, + int lut_offset1, + int dst_offset1, + int src_step1, int dst_step1) { - int gidx = get_global_id(0); - int gidy = get_global_id(1); + int x1 = get_global_id(0) << 2; + int y = get_global_id(1); - int lidx = get_local_id(0); - int lidy = get_local_id(1); - - int src_index = mad24(gidy,src_step,gidx+src_offset); - int dst_index = mad24(gidy,dst_step,gidx+dst_offset); - __local uchar l[256]; - l[lidy*16+lidx] = table[lidy*16+lidx+lut_offset]; - //mem_fence(CLK_LOCAL_MEM_FENCE); - barrier(CLK_LOCAL_MEM_FENCE); - - if(gidx Date: Tue, 24 Sep 2013 13:51:37 +0400 Subject: [PATCH 43/68] refactored and extended ocl::addWeighted --- modules/ocl/src/arithm.cpp | 76 ++-- modules/ocl/src/opencl/arithm_addWeighted.cl | 382 +------------------ 2 files changed, 51 insertions(+), 407 deletions(-) diff --git a/modules/ocl/src/arithm.cpp b/modules/ocl/src/arithm.cpp index 66180ba4db..8441d8e4bc 100644 --- a/modules/ocl/src/arithm.cpp +++ b/modules/ocl/src/arithm.cpp @@ -1795,64 +1795,66 @@ void cv::ocl::transpose(const oclMat &src, oclMat &dst) void cv::ocl::addWeighted(const oclMat &src1, double alpha, const oclMat &src2, double beta, double gama, oclMat &dst) { + Context *clCxt = src1.clCxt; + bool hasDouble = clCxt->supportsFeature(Context::CL_DOUBLE); + if (!hasDouble && src1.depth() == CV_64F) + { + CV_Error(CV_GpuNotSupported, "Selected device doesn't support double\r\n"); + return; + } + + CV_Assert(src1.size() == src2.size() && src1.type() == src2.type()); dst.create(src1.size(), src1.type()); - CV_Assert(src1.cols == src2.cols && src2.cols == dst.cols && - src1.rows == src2.rows && src2.rows == dst.rows); - CV_Assert(src1.type() == src2.type() && src1.type() == dst.type()); - Context *clCxt = src1.clCxt; int channels = dst.oclchannels(); int depth = dst.depth(); + int cols1 = src1.cols * channels; + int src1step1 = src1.step1(), src1offset1 = src1.offset / src1.elemSize1(); + int src2step1 = src2.step1(), src2offset1 = src2.offset / src1.elemSize1(); + int dststep1 = dst.step1(), dstoffset1 = dst.offset / dst.elemSize1(); - int vector_lengths[4][7] = {{4, 0, 4, 4, 4, 4, 4}, - {4, 0, 4, 4, 4, 4, 4}, - {4, 0, 4, 4, 4, 4, 4}, - {4, 0, 4, 4, 4, 4, 4} - }; - - - size_t vector_length = vector_lengths[channels - 1][depth]; - int offset_cols = (dst.offset / dst.elemSize1()) & (vector_length - 1); - int cols = divUp(dst.cols * channels + offset_cols, vector_length); + const char * const typeMap[] = { "uchar", "char", "ushort", "short", "int", "float", "double" }; + std::string buildOptions = format("-D T=%s -D WT=%s -D convertToT=convert_%s%s", + typeMap[depth], hasDouble ? "double" : "float", typeMap[depth], + depth >= CV_32F ? "" : "_sat_rte"); size_t localThreads[3] = { 256, 1, 1 }; - size_t globalThreads[3] = { cols, dst.rows, 1}; + size_t globalThreads[3] = { cols1, dst.rows, 1}; + + float alpha_f = static_cast(alpha), + beta_f = static_cast(beta), + gama_f = static_cast(gama); - int dst_step1 = dst.cols * dst.elemSize(); - int src1_step = (int) src1.step; - int src2_step = (int) src2.step; - int dst_step = (int) dst.step; - float alpha_f = alpha, beta_f = beta, gama_f = gama; vector > args; args.push_back( make_pair( sizeof(cl_mem), (void *)&src1.data )); - args.push_back( make_pair( sizeof(cl_int), (void *)&src1_step )); - args.push_back( make_pair( sizeof(cl_int), (void *)&src1.offset)); + args.push_back( make_pair( sizeof(cl_int), (void *)&src1step1 )); + args.push_back( make_pair( sizeof(cl_int), (void *)&src1offset1)); args.push_back( make_pair( sizeof(cl_mem), (void *)&src2.data )); - args.push_back( make_pair( sizeof(cl_int), (void *)&src2_step )); - args.push_back( make_pair( sizeof(cl_int), (void *)&src2.offset)); + args.push_back( make_pair( sizeof(cl_int), (void *)&src2step1 )); + args.push_back( make_pair( sizeof(cl_int), (void *)&src2offset1)); + args.push_back( make_pair( sizeof(cl_mem), (void *)&dst.data )); + args.push_back( make_pair( sizeof(cl_int), (void *)&dststep1 )); + args.push_back( make_pair( sizeof(cl_int), (void *)&dstoffset1)); - if(src1.clCxt->supportsFeature(Context::CL_DOUBLE)) - { - args.push_back( make_pair( sizeof(cl_double), (void *)&alpha )); - args.push_back( make_pair( sizeof(cl_double), (void *)&beta )); - args.push_back( make_pair( sizeof(cl_double), (void *)&gama )); - } - else + if (!hasDouble) { args.push_back( make_pair( sizeof(cl_float), (void *)&alpha_f )); args.push_back( make_pair( sizeof(cl_float), (void *)&beta_f )); args.push_back( make_pair( sizeof(cl_float), (void *)&gama_f )); } + else + { + args.push_back( make_pair( sizeof(cl_double), (void *)&alpha )); + args.push_back( make_pair( sizeof(cl_double), (void *)&beta )); + args.push_back( make_pair( sizeof(cl_double), (void *)&gama )); + } - args.push_back( make_pair( sizeof(cl_mem), (void *)&dst.data )); - args.push_back( make_pair( sizeof(cl_int), (void *)&dst_step )); - args.push_back( make_pair( sizeof(cl_int), (void *)&dst.offset)); + args.push_back( make_pair( sizeof(cl_int), (void *)&cols1 )); args.push_back( make_pair( sizeof(cl_int), (void *)&src1.rows )); - args.push_back( make_pair( sizeof(cl_int), (void *)&cols )); - args.push_back( make_pair( sizeof(cl_int), (void *)&dst_step1 )); - openCLExecuteKernel(clCxt, &arithm_addWeighted, "addWeighted", globalThreads, localThreads, args, -1, depth); + openCLExecuteKernel(clCxt, &arithm_addWeighted, "addWeighted", globalThreads, localThreads, + args, -1, -1, buildOptions.c_str()); } static void arithmetic_pow_run(const oclMat &src1, double p, oclMat &dst, string kernelName, const char **kernelString) diff --git a/modules/ocl/src/opencl/arithm_addWeighted.cl b/modules/ocl/src/opencl/arithm_addWeighted.cl index e7ed289281..159a970db4 100644 --- a/modules/ocl/src/opencl/arithm_addWeighted.cl +++ b/modules/ocl/src/opencl/arithm_addWeighted.cl @@ -42,392 +42,34 @@ // the use of this software, even if advised of the possibility of such damage. // //M*/ + #if defined (DOUBLE_SUPPORT) #ifdef cl_khr_fp64 #pragma OPENCL EXTENSION cl_khr_fp64:enable #elif defined (cl_amd_fp64) #pragma OPENCL EXTENSION cl_amd_fp64:enable #endif -typedef double F; -#else -typedef float F; #endif + ////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////addWeighted////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////// -__kernel void addWeighted_D0 (__global uchar *src1,int src1_step,int src1_offset, - __global uchar *src2, int src2_step,int src2_offset, - F alpha,F beta,F gama, - __global uchar *dst, int dst_step,int dst_offset, - int rows, int cols,int dst_step1) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - - { - - x = x << 2; -#ifdef dst_align -#undef dst_align -#endif -#define dst_align (dst_offset & 3) - int src1_index = mad24(y, src1_step, x + src1_offset - dst_align); - int src2_index = mad24(y, src2_step, x + src2_offset - dst_align); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + x & (int)0xfffffffc); - - uchar4 src1_data ,src2_data; - - src1_data.x= src1_index+0 >= 0 ? src1[src1_index+0] : 0; - src1_data.y= src1_index+1 >= 0 ? src1[src1_index+1] : 0; - src1_data.z= src1_index+2 >= 0 ? src1[src1_index+2] : 0; - src1_data.w= src1_index+3 >= 0 ? src1[src1_index+3] : 0; - - src2_data.x= src2_index+0 >= 0 ? src2[src2_index+0] : 0; - src2_data.y= src2_index+1 >= 0 ? src2[src2_index+1] : 0; - src2_data.z= src2_index+2 >= 0 ? src2[src2_index+2] : 0; - src2_data.w= src2_index+3 >= 0 ? src2[src2_index+3] : 0; - - uchar4 dst_data = *((__global uchar4 *)(dst + dst_index)); -// short4 tmp = convert_short4_sat(src1_data) * alpha + convert_short4_sat(src2_data) * beta + gama; - short4 tmp; - tmp.x = src1_data.x * alpha + src2_data.x * beta + gama; - tmp.y = src1_data.y * alpha + src2_data.y * beta + gama; - tmp.z = src1_data.z * alpha + src2_data.z * beta + gama; - tmp.w = src1_data.w * alpha + src2_data.w * beta + gama; - uchar4 tmp_data = convert_uchar4_sat(tmp); - - dst_data.x = ((dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : dst_data.x; - dst_data.y = ((dst_index + 1 >= dst_start) && (dst_index + 1 < dst_end)) ? tmp_data.y : dst_data.y; - dst_data.z = ((dst_index + 2 >= dst_start) && (dst_index + 2 < dst_end)) ? tmp_data.z : dst_data.z; - dst_data.w = ((dst_index + 3 >= dst_start) && (dst_index + 3 < dst_end)) ? tmp_data.w : dst_data.w; - - *((__global uchar4 *)(dst + dst_index)) = dst_data; - // dst[x + y * dst_step] = src1[x + y * src1_step] * alpha + src2[x + y * src2_step] * beta + gama; - } - -} - - - -__kernel void addWeighted_D2 (__global ushort *src1, int src1_step,int src1_offset, - __global ushort *src2, int src2_step,int src2_offset, - F alpha,F beta,F gama, - __global ushort *dst, int dst_step,int dst_offset, - int rows, int cols,int dst_step1) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - - { - - x = x << 2; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align ((dst_offset >> 1) & 3) - int src1_index = mad24(y, src1_step, (x << 1) + src1_offset -( dst_align << 1)); - int src2_index = mad24(y, src2_step, (x << 1) + src2_offset -( dst_align << 1)); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset +( x<< 1) & (int)0xfffffff8); - int src1_index_fix = src1_index < 0 ? 0 : src1_index; - int src2_index_fix = src2_index < 0 ? 0 : src2_index; - ushort4 src1_data = vload4(0, (__global ushort *)((__global char *)src1 + src1_index_fix)); - ushort4 src2_data = vload4(0, (__global ushort *)((__global char *)src2 + src2_index_fix)); - if(src1_index < 0) - { - ushort4 tmp; - tmp.xyzw = (src1_index == -2) ? src1_data.zwxy:src1_data.yzwx; - src1_data.xyzw = (src1_index == -1) ? src1_data.wxyz:tmp.xyzw; - } - if(src2_index < 0) - { - ushort4 tmp; - tmp.xyzw = (src2_index == -2) ? src2_data.zwxy:src2_data.yzwx; - src2_data.xyzw = (src2_index == -1) ? src2_data.wxyz:tmp.xyzw; - } - - - ushort4 dst_data = *((__global ushort4 *)((__global char *)dst + dst_index)); - // int4 tmp = convert_int4_sat(src1_data) * alpha + convert_int4_sat(src2_data) * beta + gama; - int4 tmp; - tmp.x = src1_data.x * alpha + src2_data.x * beta + gama; - tmp.y = src1_data.y * alpha + src2_data.y * beta + gama; - tmp.z = src1_data.z * alpha + src2_data.z * beta + gama; - tmp.w = src1_data.w * alpha + src2_data.w * beta + gama; - ushort4 tmp_data = convert_ushort4_sat(tmp); - dst_data.x = ((dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : dst_data.x; - dst_data.y = ((dst_index + 2 >= dst_start) && (dst_index + 2 < dst_end)) ? tmp_data.y : dst_data.y; - dst_data.z = ((dst_index + 4 >= dst_start) && (dst_index + 4 < dst_end)) ? tmp_data.z : dst_data.z; - dst_data.w = ((dst_index + 6 >= dst_start) && (dst_index + 6 < dst_end)) ? tmp_data.w : dst_data.w; - - *((__global ushort4 *)((__global char *)dst + dst_index)) = dst_data; - } - - -} - - -__kernel void addWeighted_D3 (__global short *src1, int src1_step,int src1_offset, - __global short *src2, int src2_step,int src2_offset, - F alpha,F beta,F gama, - __global short *dst, int dst_step,int dst_offset, - int rows, int cols,int dst_step1) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - - { - - x = x << 2; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align ((dst_offset >> 1) & 3) - int src1_index = mad24(y, src1_step, (x << 1) + src1_offset -( dst_align << 1)); - int src2_index = mad24(y, src2_step, (x << 1) + src2_offset -( dst_align << 1)); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset +( x<< 1) - (dst_align << 1 )); - - int src1_index_fix = src1_index < 0 ? 0 : src1_index; - int src2_index_fix = src2_index < 0 ? 0 : src2_index; - short4 src1_data = vload4(0, (__global short *)((__global char *)src1 + src1_index_fix)); - short4 src2_data = vload4(0, (__global short *)((__global char *)src2 + src2_index_fix)); - - if(src1_index < 0) - { - short4 tmp; - tmp.xyzw = (src1_index == -2) ? src1_data.zwxy:src1_data.yzwx; - src1_data.xyzw = (src1_index == -1) ? src1_data.wxyz:tmp.xyzw; - } - if(src2_index < 0) - { - short4 tmp; - tmp.xyzw = (src2_index == -2) ? src2_data.zwxy:src2_data.yzwx; - src2_data.xyzw = (src2_index == -1) ? src2_data.wxyz:tmp.xyzw; - } - short4 dst_data = *((__global short4 *)((__global char *)dst + dst_index)); - // int4 tmp = convert_int4_sat(src1_data) * alpha + convert_int4_sat(src2_data) * beta + gama; - int4 tmp; - tmp.x = src1_data.x * alpha + src2_data.x * beta + gama; - tmp.y = src1_data.y * alpha + src2_data.y * beta + gama; - tmp.z = src1_data.z * alpha + src2_data.z * beta + gama; - tmp.w = src1_data.w * alpha + src2_data.w * beta + gama; - short4 tmp_data = convert_short4_sat(tmp); - dst_data.x = ((dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : dst_data.x; - dst_data.y = ((dst_index + 2 >= dst_start) && (dst_index + 2 < dst_end)) ? tmp_data.y : dst_data.y; - dst_data.z = ((dst_index + 4 >= dst_start) && (dst_index + 4 < dst_end)) ? tmp_data.z : dst_data.z; - dst_data.w = ((dst_index + 6 >= dst_start) && (dst_index + 6 < dst_end)) ? tmp_data.w : dst_data.w; - - *((__global short4 *)((__global char *)dst + dst_index)) = dst_data; - } - -} - -__kernel void addWeighted_D4 (__global int *src1, int src1_step,int src1_offset, - __global int *src2, int src2_step,int src2_offset, - F alpha,F beta, F gama, - __global int *dst, int dst_step,int dst_offset, - int rows, int cols,int dst_step1) +__kernel void addWeighted(__global T * src1, int src1_step1, int src1_offset1, + __global T * src2, int src2_step1, int src2_offset1, + __global T * dst, int dst_step1, int dst_offset1, + WT alpha, WT beta, WT gama, + int cols1, int rows) { int x = get_global_id(0); int y = get_global_id(1); - if (x < cols && y < rows) - + if (x < cols1 && y < rows) { + int src1_index = mad24(y, src1_step1, x + src1_offset1); + int src2_index = mad24(y, src2_step1, x + src2_offset1); + int dst_index = mad24(y, dst_step1, x + dst_offset1); - x = x << 2; - -#define bitOfInt (sizeof(int)== 4 ? 2: 3) - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align ((dst_offset >> bitOfInt) & 3) - - int src1_index = mad24(y, src1_step, (x << bitOfInt) + src1_offset - (dst_align << bitOfInt)); - int src2_index = mad24(y, src2_step, (x << bitOfInt) + src2_offset - (dst_align << bitOfInt)); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + (x << bitOfInt) -(dst_align << bitOfInt)); - - int src1_index_fix = src1_index < 0 ? 0 : src1_index; - int src2_index_fix = src2_index < 0 ? 0 : src2_index; - int4 src1_data = vload4(0, (__global int *)((__global char *)src1 + src1_index_fix)); - int4 src2_data = vload4(0, (__global int *)((__global char *)src2 + src2_index_fix)); - - if(src1_index < 0) - { - int4 tmp; - tmp.xyzw = (src1_index == -2) ? src1_data.zwxy:src1_data.yzwx; - src1_data.xyzw = (src1_index == -1) ? src1_data.wxyz:tmp.xyzw; - } - if(src2_index < 0) - { - int4 tmp; - tmp.xyzw = (src2_index == -2) ? src2_data.zwxy:src2_data.yzwx; - src2_data.xyzw = (src2_index == -1) ? src2_data.wxyz:tmp.xyzw; - } - int4 dst_data = *((__global int4 *)((__global char *)dst + dst_index)); - // double4 tmp = convert_double4(src1_data) * alpha + convert_double4(src2_data) * beta + gama ; - float4 tmp; - tmp.x = src1_data.x * alpha + src2_data.x * beta + gama; - tmp.y = src1_data.y * alpha + src2_data.y * beta + gama; - tmp.z = src1_data.z * alpha + src2_data.z * beta + gama; - tmp.w = src1_data.w * alpha + src2_data.w * beta + gama; - int4 tmp_data = convert_int4_sat(tmp); - - dst_data.x = ((dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : dst_data.x; - dst_data.y = ((dst_index + 4 >= dst_start) && (dst_index + 4 < dst_end)) ? tmp_data.y : dst_data.y; - dst_data.z = ((dst_index + 8 >= dst_start) && (dst_index + 8 < dst_end)) ? tmp_data.z : dst_data.z; - dst_data.w = ((dst_index + 12 >= dst_start) && (dst_index + 12 < dst_end)) ? tmp_data.w : dst_data.w; - - *((__global int4 *)((__global char *)dst + dst_index)) = dst_data; + dst[dst_index] = convertToT(src1[src1_index]*alpha + src2[src2_index]*beta + gama); } - } - - -__kernel void addWeighted_D5 (__global float *src1,int src1_step,int src1_offset, - __global float *src2, int src2_step,int src2_offset, - F alpha,F beta, F gama, - __global float *dst, int dst_step,int dst_offset, - int rows, int cols,int dst_step1) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - - { - - x = x << 2; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align ((dst_offset >> 2) & 3) - - int src1_index = mad24(y, src1_step, (x << 2) + src1_offset - (dst_align << 2)); - int src2_index = mad24(y, src2_step, (x << 2) + src2_offset - (dst_align << 2)); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + (x << 2) -(dst_align << 2)); - - int src1_index_fix = src1_index < 0 ? 0 : src1_index; - int src2_index_fix = src2_index < 0 ? 0 : src2_index; - float4 src1_data = vload4(0, (__global float *)((__global char *)src1 + src1_index_fix)); - float4 src2_data = vload4(0, (__global float *)((__global char *)src2 + src2_index_fix)); - float4 dst_data = *((__global float4 *)((__global char *)dst + dst_index)); - if(src1_index < 0) - { - float4 tmp; - tmp.xyzw = (src1_index == -2) ? src1_data.zwxy:src1_data.yzwx; - src1_data.xyzw = (src1_index == -1) ? src1_data.wxyz:tmp.xyzw; - } - if(src2_index < 0) - { - float4 tmp; - tmp.xyzw = (src2_index == -2) ? src2_data.zwxy:src2_data.yzwx; - src2_data.xyzw = (src2_index == -1) ? src2_data.wxyz:tmp.xyzw; - } - // double4 tmp = convert_double4(src1_data) * alpha + convert_double4(src2_data) * beta + gama ; - - // float4 tmp_data =(src1_data) * alpha + (src2_data) * beta + gama ; - float4 tmp_data; - tmp_data.x = src1_data.x * alpha + src2_data.x * beta + gama; - tmp_data.y = src1_data.y * alpha + src2_data.y * beta + gama; - tmp_data.z = src1_data.z * alpha + src2_data.z * beta + gama; - tmp_data.w = src1_data.w * alpha + src2_data.w * beta + gama; - // float4 tmp_data = convert_float4(tmp); - - dst_data.x = ((dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : dst_data.x; - dst_data.y = ((dst_index + 4 >= dst_start) && (dst_index + 4 < dst_end)) ? tmp_data.y : dst_data.y; - dst_data.z = ((dst_index + 8 >= dst_start) && (dst_index + 8 < dst_end)) ? tmp_data.z : dst_data.z; - dst_data.w = ((dst_index + 12 >= dst_start) && (dst_index + 12 < dst_end)) ? tmp_data.w : dst_data.w; - - *((__global float4 *)((__global char *)dst + dst_index)) = dst_data; - } - -} - -#if defined (DOUBLE_SUPPORT) -__kernel void addWeighted_D6 (__global double *src1, int src1_step,int src1_offset, - __global double *src2, int src2_step,int src2_offset, - F alpha,F beta, F gama, - __global double *dst, int dst_step,int dst_offset, - int rows, int cols,int dst_step1) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - - { - - x = x << 2; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align ((dst_offset >> 3) & 3) - - int src1_index = mad24(y, src1_step, (x << 3) + src1_offset - (dst_align << 3)); - int src2_index = mad24(y, src2_step, (x << 3) + src2_offset - (dst_align << 3)); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + (x << 3) -(dst_align << 3)); - - int src1_index_fix = src1_index < 0 ? 0 : src1_index; - int src2_index_fix = src2_index < 0 ? 0 : src2_index; - double4 src1_data = vload4(0, (__global double *)((__global char *)src1 + src1_index_fix)); - double4 src2_data = vload4(0, (__global double *)((__global char *)src2 + src2_index_fix)); - double4 dst_data = *((__global double4 *)((__global char *)dst + dst_index)); - if(src1_index < 0) - { - double4 tmp; - tmp.xyzw = (src1_index == -2) ? src1_data.zwxy:src1_data.yzwx; - src1_data.xyzw = (src1_index == -1) ? src1_data.wxyz:tmp.xyzw; - } - if(src2_index < 0) - { - double4 tmp; - tmp.xyzw = (src2_index == -2) ? src2_data.zwxy:src2_data.yzwx; - src2_data.xyzw = (src2_index == -1) ? src2_data.wxyz:tmp.xyzw; - } - // double4 tmp_data = (src1_data) * alpha + (src2_data) * beta + gama ; - double4 tmp_data; - tmp_data.x = src1_data.x * alpha + src2_data.x * beta + gama; - tmp_data.y = src1_data.y * alpha + src2_data.y * beta + gama; - tmp_data.z = src1_data.z * alpha + src2_data.z * beta + gama; - tmp_data.w = src1_data.w * alpha + src2_data.w * beta + gama; - - dst_data.x = ((dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : dst_data.x; - dst_data.y = ((dst_index + 8 >= dst_start) && (dst_index + 8 < dst_end)) ? tmp_data.y : dst_data.y; - dst_data.z = ((dst_index + 16 >= dst_start) && (dst_index + 16 < dst_end)) ? tmp_data.z : dst_data.z; - dst_data.w = ((dst_index + 24 >= dst_start) && (dst_index + 24 < dst_end)) ? tmp_data.w : dst_data.w; - - *((__global double4 *)((__global char *)dst + dst_index)) = dst_data; - } - -} -#endif From 8e0e352d778e25f3e6a851d15f985ff133ad1597 Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Tue, 24 Sep 2013 13:54:46 +0400 Subject: [PATCH 44/68] refactored and extended binary bitwise operations --- modules/ocl/src/arithm.cpp | 325 ++------ .../ocl/src/opencl/arithm_bitwise_binary.cl | 298 +------ .../src/opencl/arithm_bitwise_binary_mask.cl | 764 +----------------- .../opencl/arithm_bitwise_binary_scalar.cl | 590 +------------- .../arithm_bitwise_binary_scalar_mask.cl | 694 +--------------- 5 files changed, 106 insertions(+), 2565 deletions(-) diff --git a/modules/ocl/src/arithm.cpp b/modules/ocl/src/arithm.cpp index 8441d8e4bc..4f6737e822 100644 --- a/modules/ocl/src/arithm.cpp +++ b/modules/ocl/src/arithm.cpp @@ -1290,7 +1290,8 @@ int cv::ocl::countNonZero(const oclMat &src) ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////bitwise_op//////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// -static void bitwise_run(const oclMat &src1, oclMat &dst, string kernelName, const char **kernelString) + +static void bitwise_unary_run(const oclMat &src1, oclMat &dst, string kernelName, const char **kernelString) { dst.create(src1.size(), src1.type()); @@ -1327,331 +1328,123 @@ static void bitwise_run(const oclMat &src1, oclMat &dst, string kernelName, cons openCLExecuteKernel(clCxt, kernelString, kernelName, globalThreads, localThreads, args, -1, depth); } +enum { AND = 0, OR, XOR }; -template -void bitwise_run(const oclMat &src1, const oclMat &src2, oclMat &dst, string kernelName, - const char **kernelString, void *_scalar, const char* _opt = NULL) +static void bitwise_binary_run(const oclMat &src1, const oclMat &src2, const Scalar& src3, const oclMat &mask, + oclMat &dst, int operationType) { - dst.create(src1.size(), src1.type()); - CV_Assert(src1.cols == src2.cols && src2.cols == dst.cols && - src1.rows == src2.rows && src2.rows == dst.rows); - - CV_Assert(src1.type() == src2.type() && src1.type() == dst.type()); - Context *clCxt = src1.clCxt; - int channels = dst.oclchannels(); - int depth = dst.depth(); - - int vector_lengths[4][7] = {{4, 4, 4, 4, 1, 1, 1}, - {4, 4, 4, 4, 1, 1, 1}, - {4, 4, 4, 4, 1, 1, 1}, - {4, 4, 4, 4, 1, 1, 1} - }; - - size_t vector_length = vector_lengths[channels - 1][depth]; - int offset_cols = (dst.offset / dst.elemSize1()) & (vector_length - 1); - int cols = divUp(dst.cols * channels + offset_cols, vector_length); - - size_t localThreads[3] = { 64, 4, 1 }; - size_t globalThreads[3] = { cols, dst.rows, 1 }; - - int dst_step1 = dst.cols * dst.elemSize(); - vector > args; - args.push_back( make_pair( sizeof(cl_mem), (void *)&src1.data )); - args.push_back( make_pair( sizeof(cl_int), (void *)&src1.step )); - args.push_back( make_pair( sizeof(cl_int), (void *)&src1.offset )); - args.push_back( make_pair( sizeof(cl_mem), (void *)&src2.data )); - args.push_back( make_pair( sizeof(cl_int), (void *)&src2.step )); - args.push_back( make_pair( sizeof(cl_int), (void *)&src2.offset )); - args.push_back( make_pair( sizeof(cl_mem), (void *)&dst.data )); - args.push_back( make_pair( sizeof(cl_int), (void *)&dst.step )); - args.push_back( make_pair( sizeof(cl_int), (void *)&dst.offset )); - args.push_back( make_pair( sizeof(cl_int), (void *)&src1.rows )); - args.push_back( make_pair( sizeof(cl_int), (void *)&cols )); - args.push_back( make_pair( sizeof(cl_int), (void *)&dst_step1 )); - - T scalar; - if(_scalar != NULL) + if (!clCxt->supportsFeature(Context::CL_DOUBLE) && src1.depth() == CV_64F) { - double scalar1 = *((double *)_scalar); - scalar = (T)scalar1; - args.push_back( make_pair( sizeof(T), (void *)&scalar )); + cout << "Selected device does not support double" << endl; + return; } - openCLExecuteKernel(clCxt, kernelString, kernelName, globalThreads, localThreads, args, -1, depth, _opt); -} -static void bitwise_run(const oclMat &src1, const oclMat &src2, oclMat &dst, - string kernelName, const char **kernelString, const char* _opt = NULL) -{ - bitwise_run(src1, src2, dst, kernelName, kernelString, (void *)NULL, _opt); -} -static void bitwise_run(const oclMat &src1, const oclMat &src2, oclMat &dst, - const oclMat &mask, string kernelName, const char **kernelString, const char* _opt = NULL) -{ - dst.create(src1.size(), src1.type()); - CV_Assert(src1.cols == src2.cols && src2.cols == dst.cols && - src1.rows == src2.rows && src2.rows == dst.rows && - src1.rows == mask.rows && src1.cols == mask.cols); - - CV_Assert(src1.type() == src2.type() && src1.type() == dst.type()); - CV_Assert(mask.type() == CV_8U); + CV_Assert(operationType >= AND && operationType <= XOR); + CV_Assert(src2.empty() || (!src2.empty() && src1.type() == src2.type() && src1.size() == src2.size())); + CV_Assert(mask.empty() || (!mask.empty() && mask.type() == CV_8UC1 && mask.size() == src1.size())); - Context *clCxt = src1.clCxt; - int channels = dst.oclchannels(); - int depth = dst.depth(); + dst.create(src1.size(), src1.type()); - int vector_lengths[4][7] = {{4, 4, 2, 2, 1, 1, 1}, - {2, 2, 1, 1, 1, 1, 1}, - {4, 4, 2, 2 , 1, 1, 1}, - {1, 1, 1, 1, 1, 1, 1} - }; + int elemSize = dst.elemSize(); + int cols1 = dst.cols * elemSize; + oclMat m; - size_t vector_length = vector_lengths[channels - 1][depth]; - int offset_cols = ((dst.offset % dst.step) / dst.elemSize()) & (vector_length - 1); - int cols = divUp(dst.cols + offset_cols, vector_length); + const char operationMap[] = { '&', '|', '^' }; + std::string kernelName("arithm_bitwise_binary"); + std::string buildOptions = format("-D Operation=%c", operationMap[operationType]); - size_t localThreads[3] = { 64, 4, 1 }; - size_t globalThreads[3] = { cols, dst.rows, 1 }; + size_t localThreads[3] = { 16, 16, 1 }; + size_t globalThreads[3] = { cols1, dst.rows, 1 }; - int dst_step1 = dst.cols * dst.elemSize(); vector > args; args.push_back( make_pair( sizeof(cl_mem), (void *)&src1.data )); args.push_back( make_pair( sizeof(cl_int), (void *)&src1.step )); args.push_back( make_pair( sizeof(cl_int), (void *)&src1.offset )); - args.push_back( make_pair( sizeof(cl_mem), (void *)&src2.data )); - args.push_back( make_pair( sizeof(cl_int), (void *)&src2.step )); - args.push_back( make_pair( sizeof(cl_int), (void *)&src2.offset )); - args.push_back( make_pair( sizeof(cl_mem), (void *)&mask.data )); - args.push_back( make_pair( sizeof(cl_int), (void *)&mask.step )); - args.push_back( make_pair( sizeof(cl_int), (void *)&mask.offset )); - args.push_back( make_pair( sizeof(cl_mem), (void *)&dst.data )); - args.push_back( make_pair( sizeof(cl_int), (void *)&dst.step )); - args.push_back( make_pair( sizeof(cl_int), (void *)&dst.offset )); - args.push_back( make_pair( sizeof(cl_int), (void *)&src1.rows )); - args.push_back( make_pair( sizeof(cl_int), (void *)&cols )); - args.push_back( make_pair( sizeof(cl_int), (void *)&dst_step1 )); - - openCLExecuteKernel(clCxt, kernelString, kernelName, globalThreads, localThreads, args, channels, depth, _opt); -} - - -template -void bitwise_scalar_run(const oclMat &src1, const Scalar &src2, oclMat &dst, - const oclMat &mask, string kernelName, const char **kernelString, int isMatSubScalar, const char* opt = NULL) -{ - dst.create(src1.size(), src1.type()); - - CV_Assert(src1.cols == dst.cols && src1.rows == dst.rows && - src1.type() == dst.type()); - - if(mask.data) + if (src2.empty()) { - CV_Assert(mask.type() == CV_8U && src1.rows == mask.rows && src1.cols == mask.cols); - } - - Context *clCxt = src1.clCxt; - int channels = dst.oclchannels(); - int depth = dst.depth(); - - WT s[4] = { saturate_cast(src2.val[0]), saturate_cast(src2.val[1]), - saturate_cast(src2.val[2]), saturate_cast(src2.val[3]) - }; - - int vector_lengths[4][7] = {{4, 4, 2, 2, 1, 1, 1}, - {2, 2, 1, 1, 1, 1, 1}, - {4, 4, 2, 2 , 1, 1, 1}, - {1, 1, 1, 1, 1, 1, 1} - }; + m.create(1, 1, dst.type()); + m.setTo(src3); - size_t vector_length = vector_lengths[channels - 1][depth]; - int offset_cols = ((dst.offset % dst.step) / dst.elemSize()) & (vector_length - 1); - int cols = divUp(dst.cols + offset_cols, vector_length); - - size_t localThreads[3] = { 64, 4, 1 }; - size_t globalThreads[3] = { cols, dst.rows, 1 }; + args.push_back( make_pair( sizeof(cl_mem), (void *)&m.data )); + args.push_back( make_pair( sizeof(cl_int), (void *)&elemSize ) ); - int dst_step1 = dst.cols * dst.elemSize(); - vector > args; - args.push_back( make_pair( sizeof(cl_mem) , (void *)&src1.data )); - args.push_back( make_pair( sizeof(cl_int) , (void *)&src1.step )); - args.push_back( make_pair( sizeof(cl_int) , (void *)&src1.offset)); - args.push_back( make_pair( sizeof(cl_mem) , (void *)&dst.data )); - args.push_back( make_pair( sizeof(cl_int) , (void *)&dst.step )); - args.push_back( make_pair( sizeof(cl_int) , (void *)&dst.offset)); - - if(mask.data) - { - args.push_back( make_pair( sizeof(cl_mem) , (void *)&mask.data )); - args.push_back( make_pair( sizeof(cl_int) , (void *)&mask.step )); - args.push_back( make_pair( sizeof(cl_int) , (void *)&mask.offset)); + kernelName += "_scalar"; } - args.push_back( make_pair( sizeof(CL_WT) , (void *)&s )); - args.push_back( make_pair( sizeof(cl_int) , (void *)&src1.rows )); - args.push_back( make_pair( sizeof(cl_int) , (void *)&cols )); - args.push_back( make_pair( sizeof(cl_int) , (void *)&dst_step1 )); - if(isMatSubScalar != 0) + else { - isMatSubScalar = isMatSubScalar > 0 ? 1 : 0; - args.push_back( make_pair( sizeof(cl_int) , (void *)&isMatSubScalar)); + args.push_back( make_pair( sizeof(cl_mem), (void *)&src2.data )); + args.push_back( make_pair( sizeof(cl_int), (void *)&src2.step )); + args.push_back( make_pair( sizeof(cl_int), (void *)&src2.offset )); } - openCLExecuteKernel(clCxt, kernelString, kernelName, globalThreads, localThreads, args, channels, depth, opt); -} + if (!mask.empty()) + { + args.push_back( make_pair( sizeof(cl_mem), (void *)&mask.data )); + args.push_back( make_pair( sizeof(cl_int), (void *)&mask.step )); + args.push_back( make_pair( sizeof(cl_int), (void *)&mask.offset )); + + if (!src2.empty()) + args.push_back( make_pair( sizeof(cl_int), (void *)&elemSize )); + kernelName += "_mask"; + } -typedef void (*BitwiseFuncS)(const oclMat &src1, const Scalar &src2, oclMat &dst, - const oclMat &mask, string kernelName, const char **kernelString, int isMatSubScalar, const char* opt); + args.push_back( make_pair( sizeof(cl_mem), (void *)&dst.data )); + args.push_back( make_pair( sizeof(cl_int), (void *)&dst.step )); + args.push_back( make_pair( sizeof(cl_int), (void *)&dst.offset )); + args.push_back( make_pair( sizeof(cl_int), (void *)&cols1 )); + args.push_back( make_pair( sizeof(cl_int), (void *)&src1.rows )); -static void bitwise_scalar(const oclMat &src1, const Scalar &src2, oclMat &dst, - const oclMat &mask, string kernelName, const char **kernelString, int isMatSubScalar, const char* opt) -{ - static BitwiseFuncS tab[8] = - { -#if 0 - bitwise_scalar_run, - bitwise_scalar_run, - bitwise_scalar_run, - bitwise_scalar_run, - bitwise_scalar_run, - bitwise_scalar_run, - bitwise_scalar_run, - 0 -#else - - bitwise_scalar_run, - bitwise_scalar_run, - bitwise_scalar_run, - bitwise_scalar_run, - bitwise_scalar_run, - bitwise_scalar_run, - bitwise_scalar_run, - 0 -#endif - }; - BitwiseFuncS func = tab[src1.depth()]; - if(func == 0) - cv::ocl::error("Unsupported arithmetic operation", __FILE__, __LINE__); - func(src1, src2, dst, mask, kernelName, kernelString, isMatSubScalar, opt); -} -static void bitwise_scalar(const oclMat &src1, const Scalar &src2, oclMat &dst, - const oclMat &mask, string kernelName, const char **kernelString, const char * opt = NULL) -{ - bitwise_scalar(src1, src2, dst, mask, kernelName, kernelString, 0, opt); + openCLExecuteKernel(clCxt, mask.empty() ? (!src2.empty() ? &arithm_bitwise_binary : &arithm_bitwise_binary_scalar) : + (!src2.empty() ? &arithm_bitwise_binary_mask : &arithm_bitwise_binary_scalar_mask), + kernelName, globalThreads, localThreads, + args, -1, -1, buildOptions.c_str()); } void cv::ocl::bitwise_not(const oclMat &src, oclMat &dst) { - if(!src.clCxt->supportsFeature(Context::CL_DOUBLE) && src.type() == CV_64F) + if (!src.clCxt->supportsFeature(Context::CL_DOUBLE) && src.type() == CV_64F) { - cout << "Selected device do not support double" << endl; + cout << "Selected device does not support double" << endl; return; } + dst.create(src.size(), src.type()); string kernelName = "arithm_bitwise_not"; - bitwise_run(src, dst, kernelName, &arithm_bitwise_not); + bitwise_unary_run(src, dst, kernelName, &arithm_bitwise_not); } void cv::ocl::bitwise_or(const oclMat &src1, const oclMat &src2, oclMat &dst, const oclMat &mask) { - // dst.create(src1.size(),src1.type()); - if(!src1.clCxt->supportsFeature(Context::CL_DOUBLE) && src1.type() == CV_64F) - { - cout << "Selected device do not support double" << endl; - return; - } - - string kernelName = mask.empty() ? "arithm_bitwise_binary" : "arithm_bitwise_binary_with_mask"; - static const char opt [] = "-D OP_BINARY=|"; - if (mask.empty()) - bitwise_run(src1, src2, dst, kernelName, &arithm_bitwise_binary, opt); - else - bitwise_run(src1, src2, dst, mask, kernelName, &arithm_bitwise_binary_mask, opt); + bitwise_binary_run(src1, src2, Scalar(), mask, dst, OR); } - void cv::ocl::bitwise_or(const oclMat &src1, const Scalar &src2, oclMat &dst, const oclMat &mask) { - if(!src1.clCxt->supportsFeature(Context::CL_DOUBLE) && src1.type() == CV_64F) - { - cout << "Selected device do not support double" << endl; - return; - } - static const char opt [] = "-D OP_BINARY=|"; - string kernelName = mask.data ? "arithm_s_bitwise_binary_with_mask" : "arithm_s_bitwise_binary"; - if (mask.data) - bitwise_scalar( src1, src2, dst, mask, kernelName, &arithm_bitwise_binary_scalar_mask, opt); - else - bitwise_scalar( src1, src2, dst, mask, kernelName, &arithm_bitwise_binary_scalar, opt); + bitwise_binary_run(src1, oclMat(), src2, mask, dst, OR); } void cv::ocl::bitwise_and(const oclMat &src1, const oclMat &src2, oclMat &dst, const oclMat &mask) { - // dst.create(src1.size(),src1.type()); - if(!src1.clCxt->supportsFeature(Context::CL_DOUBLE) && src1.type() == CV_64F) - { - cout << "Selected device do not support double" << endl; - return; - } - oclMat emptyMat; - - string kernelName = mask.empty() ? "arithm_bitwise_binary" : "arithm_bitwise_binary_with_mask"; - - static const char opt [] = "-D OP_BINARY=&"; - if (mask.empty()) - bitwise_run(src1, src2, dst, kernelName, &arithm_bitwise_binary, opt); - else - bitwise_run(src1, src2, dst, mask, kernelName, &arithm_bitwise_binary_mask, opt); + bitwise_binary_run(src1, src2, Scalar(), mask, dst, AND); } void cv::ocl::bitwise_and(const oclMat &src1, const Scalar &src2, oclMat &dst, const oclMat &mask) { - if(!src1.clCxt->supportsFeature(Context::CL_DOUBLE) && src1.type() == CV_64F) - { - cout << "Selected device do not support double" << endl; - return; - } - static const char opt [] = "-D OP_BINARY=&"; - string kernelName = mask.data ? "arithm_s_bitwise_binary_with_mask" : "arithm_s_bitwise_binary"; - if (mask.data) - bitwise_scalar(src1, src2, dst, mask, kernelName, &arithm_bitwise_binary_scalar_mask, opt); - else - bitwise_scalar(src1, src2, dst, mask, kernelName, &arithm_bitwise_binary_scalar, opt); + bitwise_binary_run(src1, oclMat(), src2, mask, dst, AND); } void cv::ocl::bitwise_xor(const oclMat &src1, const oclMat &src2, oclMat &dst, const oclMat &mask) { - if(!src1.clCxt->supportsFeature(Context::CL_DOUBLE) && src1.type() == CV_64F) - { - cout << "Selected device do not support double" << endl; - return; - } - string kernelName = mask.empty() ? "arithm_bitwise_binary" : "arithm_bitwise_binary_with_mask"; - - static const char opt [] = "-D OP_BINARY=^"; - - if (mask.empty()) - bitwise_run(src1, src2, dst, kernelName, &arithm_bitwise_binary, opt); - else - bitwise_run(src1, src2, dst, mask, kernelName, &arithm_bitwise_binary_mask, opt); + bitwise_binary_run(src1, src2, Scalar(), mask, dst, XOR); } - void cv::ocl::bitwise_xor(const oclMat &src1, const Scalar &src2, oclMat &dst, const oclMat &mask) { - - if(!src1.clCxt->supportsFeature(Context::CL_DOUBLE) && src1.type() == CV_64F) - { - cout << "Selected device do not support double" << endl; - return; - } - string kernelName = mask.data ? "arithm_s_bitwise_binary_with_mask" : "arithm_s_bitwise_binary"; - static const char opt [] = "-D OP_BINARY=^"; - if (mask.data) - bitwise_scalar( src1, src2, dst, mask, kernelName, &arithm_bitwise_binary_scalar_mask, opt); - else - bitwise_scalar( src1, src2, dst, mask, kernelName, &arithm_bitwise_binary_scalar, opt); + bitwise_binary_run(src1, oclMat(), src2, mask, dst, XOR); } oclMat cv::ocl::operator ~ (const oclMat &src) diff --git a/modules/ocl/src/opencl/arithm_bitwise_binary.cl b/modules/ocl/src/opencl/arithm_bitwise_binary.cl index 8bdd23c177..898b40a9ee 100644 --- a/modules/ocl/src/opencl/arithm_bitwise_binary.cl +++ b/modules/ocl/src/opencl/arithm_bitwise_binary.cl @@ -43,303 +43,25 @@ // the use of this software, even if advised of the possibility of such damage. // //M*/ -#if defined (DOUBLE_SUPPORT) -#ifdef cl_khr_fp64 -#pragma OPENCL EXTENSION cl_khr_fp64:enable -#elif defined (cl_amd_fp64) -#pragma OPENCL EXTENSION cl_amd_fp64:enable -#endif -#endif - -//bitwise_binary without mask for and, or, xor operators ///////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////bitwise_binary/////////////////////////////////////////// +/////////////////////////////////////////// bitwise_binary ////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////// -#ifndef OP_BINARY -#define OP_BINARY & -#endif - -__kernel void arithm_bitwise_binary_D0 (__global uchar *src1, int src1_step, int src1_offset, - __global uchar *src2, int src2_step, int src2_offset, - __global uchar *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 2; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align (dst_offset & 3) - int src1_index = mad24(y, src1_step, x + src1_offset - dst_align); - int src2_index = mad24(y, src2_step, x + src2_offset - dst_align); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + x & (int)0xfffffffc); - int src1_index_fix = src1_index < 0 ? 0 : src1_index; - int src2_index_fix = src2_index < 0 ? 0 : src2_index; - uchar4 src1_data = vload4(0, src1 + src1_index_fix); - uchar4 src2_data = vload4(0, src2 + src2_index_fix); - - if(src1_index < 0) - { - uchar4 tmp; - tmp.xyzw = (src1_index == -2) ? src1_data.zwxy:src1_data.yzwx; - src1_data.xyzw = (src1_index == -1) ? src1_data.wxyz:tmp.xyzw; - } - if(src2_index < 0) - { - uchar4 tmp; - tmp.xyzw = (src2_index == -2) ? src2_data.zwxy:src2_data.yzwx; - src2_data.xyzw = (src2_index == -1) ? src2_data.wxyz:tmp.xyzw; - } - - uchar4 dst_data = *((__global uchar4 *)(dst + dst_index)); - uchar4 tmp_data = src1_data OP_BINARY src2_data; - - dst_data.x = ((dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : dst_data.x; - dst_data.y = ((dst_index + 1 >= dst_start) && (dst_index + 1 < dst_end)) ? tmp_data.y : dst_data.y; - dst_data.z = ((dst_index + 2 >= dst_start) && (dst_index + 2 < dst_end)) ? tmp_data.z : dst_data.z; - dst_data.w = ((dst_index + 3 >= dst_start) && (dst_index + 3 < dst_end)) ? tmp_data.w : dst_data.w; - - *((__global uchar4 *)(dst + dst_index)) = dst_data; - } -} - - -__kernel void arithm_bitwise_binary_D1 (__global char *src1, int src1_step, int src1_offset, - __global char *src2, int src2_step, int src2_offset, - __global char *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 2; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align (dst_offset & 3) - int src1_index = mad24(y, src1_step, x + src1_offset - dst_align); - int src2_index = mad24(y, src2_step, x + src2_offset - dst_align); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + x & (int)0xfffffffc); - - int src1_index_fix = src1_index < 0 ? 0 : src1_index; - int src2_index_fix = src2_index < 0 ? 0 : src2_index; - char4 src1_data = vload4(0, src1 + src1_index_fix); - char4 src2_data = vload4(0, src2 + src2_index_fix); - - if(src1_index < 0) - { - char4 tmp; - tmp.xyzw = (src1_index == -2) ? src1_data.zwxy:src1_data.yzwx; - src1_data.xyzw = (src1_index == -1) ? src1_data.wxyz:tmp.xyzw; - } - if(src2_index < 0) - { - char4 tmp; - tmp.xyzw = (src2_index == -2) ? src2_data.zwxy:src2_data.yzwx; - src2_data.xyzw = (src2_index == -1) ? src2_data.wxyz:tmp.xyzw; - } - char4 dst_data = *((__global char4 *)(dst + dst_index)); - char4 tmp_data = src1_data OP_BINARY src2_data; - - dst_data.x = ((dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : dst_data.x; - dst_data.y = ((dst_index + 1 >= dst_start) && (dst_index + 1 < dst_end)) ? tmp_data.y : dst_data.y; - dst_data.z = ((dst_index + 2 >= dst_start) && (dst_index + 2 < dst_end)) ? tmp_data.z : dst_data.z; - dst_data.w = ((dst_index + 3 >= dst_start) && (dst_index + 3 < dst_end)) ? tmp_data.w : dst_data.w; - - *((__global char4 *)(dst + dst_index)) = dst_data; - } -} - - -__kernel void arithm_bitwise_binary_D2 (__global ushort *src1, int src1_step, int src1_offset, - __global ushort *src2, int src2_step, int src2_offset, - __global ushort *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) - +__kernel void arithm_bitwise_binary(__global uchar * src1, int src1_step, int src1_offset, + __global uchar * src2, int src2_step, int src2_offset, + __global uchar * dst, int dst_step, int dst_offset, + int cols1, int rows) { int x = get_global_id(0); int y = get_global_id(1); - if (x < cols && y < rows) + if (x < cols1 && y < rows) { - x = x << 2; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align ((dst_offset >> 1) & 3) - int src1_index = mad24(y, src1_step, (x << 1) + src1_offset - (dst_align << 1)); - int src2_index = mad24(y, src2_step, (x << 1) + src2_offset - (dst_align << 1)); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + (x << 1) & (int)0xfffffff8); - - int src1_index_fix = src1_index < 0 ? 0 : src1_index; - int src2_index_fix = src2_index < 0 ? 0 : src2_index; - ushort4 src1_data = vload4(0, (__global ushort *)((__global char *)src1 + src1_index_fix)); - ushort4 src2_data = vload4(0, (__global ushort *)((__global char *)src2 + src2_index_fix)); - - if(src1_index < 0) - { - ushort4 tmp; - tmp.xyzw = (src1_index == -2) ? src1_data.zwxy:src1_data.yzwx; - src1_data.xyzw = (src1_index == -1) ? src1_data.wxyz:tmp.xyzw; - } - if(src2_index < 0) - { - ushort4 tmp; - tmp.xyzw = (src2_index == -2) ? src2_data.zwxy:src2_data.yzwx; - src2_data.xyzw = (src2_index == -1) ? src2_data.wxyz:tmp.xyzw; - } - ushort4 dst_data = *((__global ushort4 *)((__global char *)dst + dst_index)); - ushort4 tmp_data = src1_data OP_BINARY src2_data; - - dst_data.x = ((dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : dst_data.x; - dst_data.y = ((dst_index + 2 >= dst_start) && (dst_index + 2 < dst_end)) ? tmp_data.y : dst_data.y; - dst_data.z = ((dst_index + 4 >= dst_start) && (dst_index + 4 < dst_end)) ? tmp_data.z : dst_data.z; - dst_data.w = ((dst_index + 6 >= dst_start) && (dst_index + 6 < dst_end)) ? tmp_data.w : dst_data.w; - - *((__global ushort4 *)((__global char *)dst + dst_index)) = dst_data; - } -} - - - -__kernel void arithm_bitwise_binary_D3 (__global short *src1, int src1_step, int src1_offset, - __global short *src2, int src2_step, int src2_offset, - __global short *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) - -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 2; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align ((dst_offset >> 1) & 3) - int src1_index = mad24(y, src1_step, (x << 1) + src1_offset - (dst_align << 1)); - int src2_index = mad24(y, src2_step, (x << 1) + src2_offset - (dst_align << 1)); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + (x << 1) & (int)0xfffffff8); - - int src1_index_fix = src1_index < 0 ? 0 : src1_index; - int src2_index_fix = src2_index < 0 ? 0 : src2_index; - short4 src1_data = vload4(0, (__global short *)((__global char *)src1 + src1_index_fix)); - short4 src2_data = vload4(0, (__global short *)((__global char *)src2 + src2_index_fix)); - - if(src1_index < 0) - { - short4 tmp; - tmp.xyzw = (src1_index == -2) ? src1_data.zwxy:src1_data.yzwx; - src1_data.xyzw = (src1_index == -1) ? src1_data.wxyz:tmp.xyzw; - } - if(src2_index < 0) - { - short4 tmp; - tmp.xyzw = (src2_index == -2) ? src2_data.zwxy:src2_data.yzwx; - src2_data.xyzw = (src2_index == -1) ? src2_data.wxyz:tmp.xyzw; - } - short4 dst_data = *((__global short4 *)((__global char *)dst + dst_index)); - short4 tmp_data = src1_data OP_BINARY src2_data; - - dst_data.x = ((dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : dst_data.x; - dst_data.y = ((dst_index + 2 >= dst_start) && (dst_index + 2 < dst_end)) ? tmp_data.y : dst_data.y; - dst_data.z = ((dst_index + 4 >= dst_start) && (dst_index + 4 < dst_end)) ? tmp_data.z : dst_data.z; - dst_data.w = ((dst_index + 6 >= dst_start) && (dst_index + 6 < dst_end)) ? tmp_data.w : dst_data.w; - - *((__global short4 *)((__global char *)dst + dst_index)) = dst_data; - } -} - - - -__kernel void arithm_bitwise_binary_D4 (__global int *src1, int src1_step, int src1_offset, - __global int *src2, int src2_step, int src2_offset, - __global int *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 2) + src1_offset); - int src2_index = mad24(y, src2_step, (x << 2) + src2_offset); - int dst_index = mad24(y, dst_step, (x << 2) + dst_offset); - - int data1 = *((__global int *)((__global char *)src1 + src1_index)); - int data2 = *((__global int *)((__global char *)src2 + src2_index)); - int tmp = data1 OP_BINARY data2; - - *((__global int *)((__global char *)dst + dst_index)) = tmp; - } -} - -__kernel void arithm_bitwise_binary_D5 (__global char *src1, int src1_step, int src1_offset, - __global char *src2, int src2_step, int src2_offset, - __global char *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 2) + src1_offset); - int src2_index = mad24(y, src2_step, (x << 2) + src2_offset); - int dst_index = mad24(y, dst_step, (x << 2) + dst_offset); - - char4 data1 = *((__global char4 *)((__global char *)src1 + src1_index)); - char4 data2 = *((__global char4 *)((__global char *)src2 + src2_index)); - char4 tmp = data1 OP_BINARY data2; - - *((__global char4 *)((__global char *)dst + dst_index)) = tmp; - } -} - -#if defined (DOUBLE_SUPPORT) -__kernel void arithm_bitwise_binary_D6 (__global char *src1, int src1_step, int src1_offset, - __global char *src2, int src2_step, int src2_offset, - __global char *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 3) + src1_offset); - int src2_index = mad24(y, src2_step, (x << 3) + src2_offset); - int dst_index = mad24(y, dst_step, (x << 3) + dst_offset); - - char8 data1 = *((__global char8 *)((__global char *)src1 + src1_index)); - char8 data2 = *((__global char8 *)((__global char *)src2 + src2_index)); + int src1_index = mad24(y, src1_step, x + src1_offset); + int src2_index = mad24(y, src2_step, x + src2_offset); + int dst_index = mad24(y, dst_step, dst_offset + x); - *((__global char8 *)((__global char *)dst + dst_index)) = data1 OP_BINARY data2; + dst[dst_index] = src1[src1_index] Operation src2[src2_index]; } } -#endif diff --git a/modules/ocl/src/opencl/arithm_bitwise_binary_mask.cl b/modules/ocl/src/opencl/arithm_bitwise_binary_mask.cl index 60cd188203..622ab5b113 100644 --- a/modules/ocl/src/opencl/arithm_bitwise_binary_mask.cl +++ b/modules/ocl/src/opencl/arithm_bitwise_binary_mask.cl @@ -43,767 +43,31 @@ // the use of this software, even if advised of the possibility of such damage. // //M*/ -#if defined (DOUBLE_SUPPORT) -#ifdef cl_khr_fp64 -#pragma OPENCL EXTENSION cl_khr_fp64:enable -#elif defined (cl_amd_fp64) -#pragma OPENCL EXTENSION cl_amd_fp64:enable -#endif -#endif - -#ifndef OP_BINARY -#define OP_BINARY & -#endif ////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////bitwise_binary//////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////// -/**************************************bitwise_binary with mask**************************************/ -__kernel void arithm_bitwise_binary_with_mask_C1_D0 ( - __global uchar *src1, int src1_step, int src1_offset, - __global uchar *src2, int src2_step, int src2_offset, - __global uchar *mask, int mask_step, int mask_offset, - __global uchar *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 2; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align (dst_offset & 3) - int src1_index = mad24(y, src1_step, x + src1_offset - dst_align); - int src2_index = mad24(y, src2_step, x + src2_offset - dst_align); - int mask_index = mad24(y, mask_step, x + mask_offset - dst_align); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + x & (int)0xfffffffc); - - uchar4 src1_data = vload4(0, src1 + src1_index); - uchar4 src2_data = vload4(0, src2 + src2_index); - uchar4 mask_data = vload4(0, mask + mask_index); - uchar4 data = *((__global uchar4 *)(dst + dst_index)); - uchar4 tmp_data = src1_data OP_BINARY src2_data; - - data.x = ((mask_data.x) && (dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : data.x; - data.y = ((mask_data.y) && (dst_index + 1 >= dst_start) && (dst_index + 1 < dst_end)) ? tmp_data.y : data.y; - data.z = ((mask_data.z) && (dst_index + 2 >= dst_start) && (dst_index + 2 < dst_end)) ? tmp_data.z : data.z; - data.w = ((mask_data.w) && (dst_index + 3 >= dst_start) && (dst_index + 3 < dst_end)) ? tmp_data.w : data.w; - - *((__global uchar4 *)(dst + dst_index)) = data; - } -} - - - -__kernel void arithm_bitwise_binary_with_mask_C1_D1 ( - __global char *src1, int src1_step, int src1_offset, - __global char *src2, int src2_step, int src2_offset, - __global uchar *mask, int mask_step, int mask_offset, - __global char *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) +__kernel void arithm_bitwise_binary_mask(__global uchar * src1, int src1_step, int src1_offset, + __global uchar * src2, int src2_step, int src2_offset, + __global uchar * mask, int mask_step, int mask_offset, int elemSize, + __global uchar * dst, int dst_step, int dst_offset, + int cols1, int rows) { - int x = get_global_id(0); int y = get_global_id(1); - if (x < cols && y < rows) + if (x < cols1 && y < rows) { - x = x << 2; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align (dst_offset & 3) - int src1_index = mad24(y, src1_step, x + src1_offset - dst_align); - int src2_index = mad24(y, src2_step, x + src2_offset - dst_align); - int mask_index = mad24(y, mask_step, x + mask_offset - dst_align); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + x & (int)0xfffffffc); - - char4 src1_data = vload4(0, src1 + src1_index); - char4 src2_data = vload4(0, src2 + src2_index); - uchar4 mask_data = vload4(0, mask + mask_index); - - char4 data = *((__global char4 *)(dst + dst_index)); - char4 tmp_data = src1_data OP_BINARY src2_data; - - data.x = convert_char((mask_data.x) && (dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : data.x; - data.y = convert_char((mask_data.y) && (dst_index + 1 >= dst_start) && (dst_index + 1 < dst_end)) ? tmp_data.y : data.y; - data.z = convert_char((mask_data.z) && (dst_index + 2 >= dst_start) && (dst_index + 2 < dst_end)) ? tmp_data.z : data.z; - data.w = convert_char((mask_data.w) && (dst_index + 3 >= dst_start) && (dst_index + 3 < dst_end)) ? tmp_data.w : data.w; - - *((__global char4 *)(dst + dst_index)) = data; - } -} - - - -__kernel void arithm_bitwise_binary_with_mask_C1_D2 ( - __global ushort *src1, int src1_step, int src1_offset, - __global ushort *src2, int src2_step, int src2_offset, - __global uchar *mask, int mask_step, int mask_offset, - __global ushort *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 1; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align ((dst_offset / 2) & 1) - int src1_index = mad24(y, src1_step, (x << 1) + src1_offset - (dst_align << 1)); - int src2_index = mad24(y, src2_step, (x << 1) + src2_offset - (dst_align << 1)); - int mask_index = mad24(y, mask_step, x + mask_offset - dst_align); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + (x << 1) & (int)0xfffffffc); - - ushort2 src1_data = vload2(0, (__global ushort *)((__global char *)src1 + src1_index)); - ushort2 src2_data = vload2(0, (__global ushort *)((__global char *)src2 + src2_index)); - uchar2 mask_data = vload2(0, mask + mask_index); - - ushort2 data = *((__global ushort2 *)((__global uchar *)dst + dst_index)); - ushort2 tmp_data = src1_data OP_BINARY src2_data; - - data.x = convert_ushort((mask_data.x) && (dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : data.x; - data.y = convert_ushort((mask_data.y) && (dst_index + 2 >= dst_start) && (dst_index + 2 < dst_end)) ? tmp_data.y : data.y; - - *((__global ushort2 *)((__global uchar *)dst + dst_index)) = data; - } -} - - - -__kernel void arithm_bitwise_binary_with_mask_C1_D3 ( - __global short *src1, int src1_step, int src1_offset, - __global short *src2, int src2_step, int src2_offset, - __global uchar *mask, int mask_step, int mask_offset, - __global short *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 1; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align ((dst_offset / 2) & 1) - int src1_index = mad24(y, src1_step, (x << 1) + src1_offset - (dst_align << 1)); - int src2_index = mad24(y, src2_step, (x << 1) + src2_offset - (dst_align << 1)); - int mask_index = mad24(y, mask_step, x + mask_offset - dst_align); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + (x << 1) & (int)0xfffffffc); - - short2 src1_data = vload2(0, (__global short *)((__global char *)src1 + src1_index)); - short2 src2_data = vload2(0, (__global short *)((__global char *)src2 + src2_index)); - uchar2 mask_data = vload2(0, mask + mask_index); - - short2 data = *((__global short2 *)((__global uchar *)dst + dst_index)); - short2 tmp_data = src1_data OP_BINARY src2_data; - - data.x = convert_short((mask_data.x) && (dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : data.x; - data.y = convert_short((mask_data.y) && (dst_index + 2 >= dst_start) && (dst_index + 2 < dst_end)) ? tmp_data.y : data.y; - - *((__global short2 *)((__global uchar *)dst + dst_index)) = data; - } -} - - - -__kernel void arithm_bitwise_binary_with_mask_C1_D4 ( - __global int *src1, int src1_step, int src1_offset, - __global int *src2, int src2_step, int src2_offset, - __global uchar *mask, int mask_step, int mask_offset, - __global int *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 2) + src1_offset); - int src2_index = mad24(y, src2_step, (x << 2) + src2_offset); - int mask_index = mad24(y, mask_step, x + mask_offset); - int dst_index = mad24(y, dst_step, (x << 2) + dst_offset); - - uchar mask_data = *(mask + mask_index); - - int src_data1 = *((__global int *)((__global char *)src1 + src1_index)); - int src_data2 = *((__global int *)((__global char *)src2 + src2_index)); - int dst_data = *((__global int *)((__global char *)dst + dst_index)); - - int data = src_data1 OP_BINARY src_data2; - data = mask_data ? data : dst_data; - - *((__global int *)((__global char *)dst + dst_index)) = data; - } -} - - - -__kernel void arithm_bitwise_binary_with_mask_C1_D5 ( - __global char *src1, int src1_step, int src1_offset, - __global char *src2, int src2_step, int src2_offset, - __global uchar *mask, int mask_step, int mask_offset, - __global char *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 2) + src1_offset); - int src2_index = mad24(y, src2_step, (x << 2) + src2_offset); - int mask_index = mad24(y, mask_step, x + mask_offset); - int dst_index = mad24(y, dst_step, (x << 2) + dst_offset); - - uchar mask_data = *(mask + mask_index); - - char4 src_data1 = *((__global char4 *)((__global char *)src1 + src1_index)); - char4 src_data2 = *((__global char4 *)((__global char *)src2 + src2_index)); - char4 dst_data = *((__global char4 *)((__global char *)dst + dst_index)); - - char4 data = src_data1 OP_BINARY src_data2; - data = mask_data ? data : dst_data; - - *((__global char4 *)((__global char *)dst + dst_index)) = data; - } -} - - - -__kernel void arithm_bitwise_binary_with_mask_C1_D6 ( - __global char *src1, int src1_step, int src1_offset, - __global char *src2, int src2_step, int src2_offset, - __global uchar *mask, int mask_step, int mask_offset, - __global char *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 3) + src1_offset); - int src2_index = mad24(y, src2_step, (x << 3) + src2_offset); - int mask_index = mad24(y, mask_step, x + mask_offset); - int dst_index = mad24(y, dst_step, (x << 3) + dst_offset); - - uchar mask_data = *(mask + mask_index); - - char8 src_data1 = *((__global char8 *)((__global char *)src1 + src1_index)); - char8 src_data2 = *((__global char8 *)((__global char *)src2 + src2_index)); - char8 dst_data = *((__global char8 *)((__global char *)dst + dst_index)); - - char8 data = src_data1 OP_BINARY src_data2; - data = mask_data ? data : dst_data; - - *((__global char8 *)((__global char *)dst + dst_index)) = data; - } - -} - - - -__kernel void arithm_bitwise_binary_with_mask_C2_D0 ( - __global uchar *src1, int src1_step, int src1_offset, - __global uchar *src2, int src2_step, int src2_offset, - __global uchar *mask, int mask_step, int mask_offset, - __global uchar *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 1; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align ((dst_offset / 2) & 1) - int src1_index = mad24(y, src1_step, (x << 1) + src1_offset - (dst_align << 1)); - int src2_index = mad24(y, src2_step, (x << 1) + src2_offset - (dst_align << 1)); - int mask_index = mad24(y, mask_step, x + mask_offset - dst_align); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + (x << 1) & (int)0xfffffffc); - - uchar4 src1_data = vload4(0, src1 + src1_index); - uchar4 src2_data = vload4(0, src2 + src2_index); - uchar2 mask_data = vload2(0, mask + mask_index); - - uchar4 data = *((__global uchar4 *)(dst + dst_index)); - uchar4 tmp_data = src1_data OP_BINARY src2_data; - - data.xy = ((mask_data.x) && (dst_index + 0 >= dst_start)) ? tmp_data.xy : data.xy; - data.zw = ((mask_data.y) && (dst_index + 2 < dst_end )) ? tmp_data.zw : data.zw; - - *((__global uchar4 *)(dst + dst_index)) = data; - } -} - - -__kernel void arithm_bitwise_binary_with_mask_C2_D1 ( - __global char *src1, int src1_step, int src1_offset, - __global char *src2, int src2_step, int src2_offset, - __global uchar *mask, int mask_step, int mask_offset, - __global char *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 1; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align ((dst_offset / 2) & 1) - int src1_index = mad24(y, src1_step, (x << 1) + src1_offset - (dst_align << 1)); - int src2_index = mad24(y, src2_step, (x << 1) + src2_offset - (dst_align << 1)); - int mask_index = mad24(y, mask_step, x + mask_offset - dst_align); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + (x << 1) & (int)0xfffffffc); - - char4 src1_data = vload4(0, src1 + src1_index); - char4 src2_data = vload4(0, src2 + src2_index); - uchar2 mask_data = vload2(0, mask + mask_index); - - char4 data = *((__global char4 *)(dst + dst_index)); - char4 tmp_data = src1_data OP_BINARY src2_data; - - data.xy = ((mask_data.x) && (dst_index + 0 >= dst_start)) ? tmp_data.xy : data.xy; - data.zw = ((mask_data.y) && (dst_index + 2 < dst_end )) ? tmp_data.zw : data.zw; - - *((__global char4 *)(dst + dst_index)) = data; - } -} - -__kernel void arithm_bitwise_binary_with_mask_C2_D2 ( - __global ushort *src1, int src1_step, int src1_offset, - __global ushort *src2, int src2_step, int src2_offset, - __global uchar *mask, int mask_step, int mask_offset, - __global ushort *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 2) + src1_offset); - int src2_index = mad24(y, src2_step, (x << 2) + src2_offset); - int mask_index = mad24(y, mask_step, x + mask_offset); - int dst_index = mad24(y, dst_step, (x << 2) + dst_offset); - - uchar mask_data = *(mask + mask_index); - - ushort2 src_data1 = *((__global ushort2 *)((__global char *)src1 + src1_index)); - ushort2 src_data2 = *((__global ushort2 *)((__global char *)src2 + src2_index)); - ushort2 dst_data = *((__global ushort2 *)((__global char *)dst + dst_index)); - - ushort2 data = src_data1 OP_BINARY src_data2; - data = mask_data ? data : dst_data; - - *((__global ushort2 *)((__global char *)dst + dst_index)) = data; - } -} -__kernel void arithm_bitwise_binary_with_mask_C2_D3 ( - __global short *src1, int src1_step, int src1_offset, - __global short *src2, int src2_step, int src2_offset, - __global uchar *mask, int mask_step, int mask_offset, - __global short *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 2) + src1_offset); - int src2_index = mad24(y, src2_step, (x << 2) + src2_offset); - int mask_index = mad24(y, mask_step, x + mask_offset); - int dst_index = mad24(y, dst_step, (x << 2) + dst_offset); - - uchar mask_data = *(mask + mask_index); - - short2 src_data1 = *((__global short2 *)((__global char *)src1 + src1_index)); - short2 src_data2 = *((__global short2 *)((__global char *)src2 + src2_index)); - short2 dst_data = *((__global short2 *)((__global char *)dst + dst_index)); - - short2 data = src_data1 OP_BINARY src_data2; - data = mask_data ? data : dst_data; - - *((__global short2 *)((__global char *)dst + dst_index)) = data; - } -} -__kernel void arithm_bitwise_binary_with_mask_C2_D4 ( - __global int *src1, int src1_step, int src1_offset, - __global int *src2, int src2_step, int src2_offset, - __global uchar *mask, int mask_step, int mask_offset, - __global int *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 3) + src1_offset); - int src2_index = mad24(y, src2_step, (x << 3) + src2_offset); - int mask_index = mad24(y, mask_step, x + mask_offset); - int dst_index = mad24(y, dst_step, (x << 3) + dst_offset); - - uchar mask_data = *(mask + mask_index); - - int2 src_data1 = *((__global int2 *)((__global char *)src1 + src1_index)); - int2 src_data2 = *((__global int2 *)((__global char *)src2 + src2_index)); - int2 dst_data = *((__global int2 *)((__global char *)dst + dst_index)); - - int2 data = src_data1 OP_BINARY src_data2; - data = mask_data ? data : dst_data; - - *((__global int2 *)((__global char *)dst + dst_index)) = data; - } -} -__kernel void arithm_bitwise_binary_with_mask_C2_D5 ( - __global char *src1, int src1_step, int src1_offset, - __global char *src2, int src2_step, int src2_offset, - __global uchar *mask, int mask_step, int mask_offset, - __global char *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 3) + src1_offset); - int src2_index = mad24(y, src2_step, (x << 3) + src2_offset); - int mask_index = mad24(y, mask_step, x + mask_offset); - int dst_index = mad24(y, dst_step, (x << 3) + dst_offset); - - uchar mask_data = *(mask + mask_index); - - char8 src_data1 = *((__global char8 *)((__global char *)src1 + src1_index)); - char8 src_data2 = *((__global char8 *)((__global char *)src2 + src2_index)); - char8 dst_data = *((__global char8 *)((__global char *)dst + dst_index)); - - char8 data = src_data1 OP_BINARY src_data2; - data = mask_data ? data : dst_data; - - *((__global char8 *)((__global char *)dst + dst_index)) = data; - } -} - -__kernel void arithm_bitwise_binary_with_mask_C2_D6 ( - __global char *src1, int src1_step, int src1_offset, - __global char *src2, int src2_step, int src2_offset, - __global uchar *mask, int mask_step, int mask_offset, - __global char *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 4) + src1_offset); - int src2_index = mad24(y, src2_step, (x << 4) + src2_offset); - int mask_index = mad24(y, mask_step, x + mask_offset); - int dst_index = mad24(y, dst_step, (x << 4) + dst_offset); - - uchar mask_data = *(mask + mask_index); - - char16 src_data1 = *((__global char16 *)((__global char *)src1 + src1_index)); - char16 src_data2 = *((__global char16 *)((__global char *)src2 + src2_index)); - char16 dst_data = *((__global char16 *)((__global char *)dst + dst_index)); - - char16 data = src_data1 OP_BINARY src_data2; - data = mask_data ? data : dst_data; - - *((__global char16 *)((__global char *)dst + dst_index)) = data; - } -} - - -__kernel void arithm_bitwise_binary_with_mask_C4_D0 ( - __global uchar *src1, int src1_step, int src1_offset, - __global uchar *src2, int src2_step, int src2_offset, - __global uchar *mask, int mask_step, int mask_offset, - __global uchar *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 2) + src1_offset); - int src2_index = mad24(y, src2_step, (x << 2) + src2_offset); - int mask_index = mad24(y, mask_step, x + mask_offset); - int dst_index = mad24(y, dst_step, (x << 2) + dst_offset); - - uchar mask_data = *(mask + mask_index); - - uchar4 src_data1 = *((__global uchar4 *)(src1 + src1_index)); - uchar4 src_data2 = *((__global uchar4 *)(src2 + src2_index)); - uchar4 dst_data = *((__global uchar4 *)(dst + dst_index)); - - uchar4 data = src_data1 OP_BINARY src_data2; - data = mask_data ? data : dst_data; - - *((__global uchar4 *)(dst + dst_index)) = data; - } -} - - -__kernel void arithm_bitwise_binary_with_mask_C4_D1 ( - __global char *src1, int src1_step, int src1_offset, - __global char *src2, int src2_step, int src2_offset, - __global uchar *mask, int mask_step, int mask_offset, - __global char *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 2) + src1_offset); - int src2_index = mad24(y, src2_step, (x << 2) + src2_offset); - int mask_index = mad24(y, mask_step, x + mask_offset); - int dst_index = mad24(y, dst_step, (x << 2) + dst_offset); - - uchar mask_data = *(mask + mask_index); - - char4 src_data1 = *((__global char4 *)(src1 + src1_index)); - char4 src_data2 = *((__global char4 *)(src2 + src2_index)); - char4 dst_data = *((__global char4 *)(dst + dst_index)); - - char4 data = src_data1 OP_BINARY src_data2; - data = mask_data ? data : dst_data; - - *((__global char4 *)(dst + dst_index)) = data; - } -} - -__kernel void arithm_bitwise_binary_with_mask_C4_D2 ( - __global ushort *src1, int src1_step, int src1_offset, - __global ushort *src2, int src2_step, int src2_offset, - __global uchar *mask, int mask_step, int mask_offset, - __global ushort *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 3) + src1_offset); - int src2_index = mad24(y, src2_step, (x << 3) + src2_offset); - int mask_index = mad24(y, mask_step, x + mask_offset); - int dst_index = mad24(y, dst_step, (x << 3) + dst_offset); - - uchar mask_data = *(mask + mask_index); - - ushort4 src_data1 = *((__global ushort4 *)((__global char *)src1 + src1_index)); - ushort4 src_data2 = *((__global ushort4 *)((__global char *)src2 + src2_index)); - ushort4 dst_data = *((__global ushort4 *)((__global char *)dst + dst_index)); - - ushort4 data = src_data1 OP_BINARY src_data2; - data = mask_data ? data : dst_data; - - *((__global ushort4 *)((__global char *)dst + dst_index)) = data; - } -} -__kernel void arithm_bitwise_binary_with_mask_C4_D3 ( - __global short *src1, int src1_step, int src1_offset, - __global short *src2, int src2_step, int src2_offset, - __global uchar *mask, int mask_step, int mask_offset, - __global short *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 3) + src1_offset); - int src2_index = mad24(y, src2_step, (x << 3) + src2_offset); - int mask_index = mad24(y, mask_step, x + mask_offset); - int dst_index = mad24(y, dst_step, (x << 3) + dst_offset); - - uchar mask_data = *(mask + mask_index); - - short4 src_data1 = *((__global short4 *)((__global char *)src1 + src1_index)); - short4 src_data2 = *((__global short4 *)((__global char *)src2 + src2_index)); - short4 dst_data = *((__global short4 *)((__global char *)dst + dst_index)); - - short4 data = src_data1 OP_BINARY src_data2; - data = mask_data ? data : dst_data; - - *((__global short4 *)((__global char *)dst + dst_index)) = data; - } -} -__kernel void arithm_bitwise_binary_with_mask_C4_D4 ( - __global int *src1, int src1_step, int src1_offset, - __global int *src2, int src2_step, int src2_offset, - __global uchar *mask, int mask_step, int mask_offset, - __global int *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 4) + src1_offset); - int src2_index = mad24(y, src2_step, (x << 4) + src2_offset); - int mask_index = mad24(y, mask_step, x + mask_offset); - int dst_index = mad24(y, dst_step, (x << 4) + dst_offset); - - uchar mask_data = *(mask + mask_index); - - int4 src_data1 = *((__global int4 *)((__global char *)src1 + src1_index)); - int4 src_data2 = *((__global int4 *)((__global char *)src2 + src2_index)); - int4 dst_data = *((__global int4 *)((__global char *)dst + dst_index)); - - int4 data = src_data1 OP_BINARY src_data2; - data = mask_data ? data : dst_data; - - *((__global int4 *)((__global char *)dst + dst_index)) = data; - } -} -__kernel void arithm_bitwise_binary_with_mask_C4_D5 ( - __global char *src1, int src1_step, int src1_offset, - __global char *src2, int src2_step, int src2_offset, - __global uchar *mask, int mask_step, int mask_offset, - __global char *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 4) + src1_offset); - int src2_index = mad24(y, src2_step, (x << 4) + src2_offset); - int mask_index = mad24(y, mask_step, x + mask_offset); - int dst_index = mad24(y, dst_step, (x << 4) + dst_offset); - - uchar mask_data = *(mask + mask_index); - - char16 src_data1 = *((__global char16 *)((__global char *)src1 + src1_index)); - char16 src_data2 = *((__global char16 *)((__global char *)src2 + src2_index)); - char16 dst_data = *((__global char16 *)((__global char *)dst + dst_index)); - - char16 data = src_data1 OP_BINARY src_data2; - data = mask_data ? data : dst_data; - - *((__global char16 *)((__global char *)dst + dst_index)) = data; - } -} -#if defined (DOUBLE_SUPPORT) -__kernel void arithm_bitwise_binary_with_mask_C4_D6 ( - __global char *src1, int src1_step, int src1_offset, - __global char *src2, int src2_step, int src2_offset, - __global uchar *mask, int mask_step, int mask_offset, - __global char *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 5) + src1_offset); - int src2_index = mad24(y, src2_step, (x << 5) + src2_offset); - int mask_index = mad24(y, mask_step, x + mask_offset); - int dst_index = mad24(y, dst_step, (x << 5) + dst_offset); - - uchar mask_data = *(mask + mask_index); - - char8 src_data1_0 = *((__global char8 *)((__global char *)src1 + src1_index + 0)); - char8 src_data1_1 = *((__global char8 *)((__global char *)src1 + src1_index + 8)); - char8 src_data1_2 = *((__global char8 *)((__global char *)src1 + src1_index + 16)); - char8 src_data1_3 = *((__global char8 *)((__global char *)src1 + src1_index + 24)); - - char8 src_data2_0 = *((__global char8 *)((__global char *)src2 + src2_index + 0)); - char8 src_data2_1 = *((__global char8 *)((__global char *)src2 + src2_index + 8)); - char8 src_data2_2 = *((__global char8 *)((__global char *)src2 + src2_index + 16)); - char8 src_data2_3 = *((__global char8 *)((__global char *)src2 + src2_index + 24)); - - char8 dst_data_0 = *((__global char8 *)((__global char *)dst + dst_index + 0)); - char8 dst_data_1 = *((__global char8 *)((__global char *)dst + dst_index + 8)); - char8 dst_data_2 = *((__global char8 *)((__global char *)dst + dst_index + 16)); - char8 dst_data_3 = *((__global char8 *)((__global char *)dst + dst_index + 24)); - - char8 data_0 = src_data1_0 OP_BINARY src_data2_0; - char8 data_1 = src_data1_1 OP_BINARY src_data2_1; - char8 data_2 = src_data1_2 OP_BINARY src_data2_2; - char8 data_3 = src_data1_3 OP_BINARY src_data2_3; + int mask_index = mad24(y, mask_step, mask_offset + (x / elemSize)); - data_0 = mask_data ? data_0 : dst_data_0; - data_1 = mask_data ? data_1 : dst_data_1; - data_2 = mask_data ? data_2 : dst_data_2; - data_3 = mask_data ? data_3 : dst_data_3; + if (mask[mask_index]) + { + int src1_index = mad24(y, src1_step, x + src1_offset); + int src2_index = mad24(y, src2_step, x + src2_offset); + int dst_index = mad24(y, dst_step, x + dst_offset); - *((__global char8 *)((__global char *)dst + dst_index + 0)) = data_0; - *((__global char8 *)((__global char *)dst + dst_index + 8)) = data_1; - *((__global char8 *)((__global char *)dst + dst_index + 16)) = data_2; - *((__global char8 *)((__global char *)dst + dst_index + 24)) = data_3; + dst[dst_index] = src1[src1_index] Operation src2[src2_index]; + } } } -#endif diff --git a/modules/ocl/src/opencl/arithm_bitwise_binary_scalar.cl b/modules/ocl/src/opencl/arithm_bitwise_binary_scalar.cl index 5fa25004d5..c17b412a6d 100644 --- a/modules/ocl/src/opencl/arithm_bitwise_binary_scalar.cl +++ b/modules/ocl/src/opencl/arithm_bitwise_binary_scalar.cl @@ -43,596 +43,26 @@ // the use of this software, even if advised of the possibility of such damage. // // -#if defined (DOUBLE_SUPPORT) -#ifdef cl_khr_fp64 -#pragma OPENCL EXTENSION cl_khr_fp64:enable -#elif defined (cl_amd_fp64) -#pragma OPENCL EXTENSION cl_amd_fp64:enable -#endif -#endif - -#ifndef OP_BINARY -#define OP_BINARY & -#endif /////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////bitwise_binary///////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////// -/******************************bitwise binary with scalar without mask********************************/ -__kernel void arithm_s_bitwise_binary_C1_D0 ( - __global uchar *src1, int src1_step, int src1_offset, - __global uchar *dst, int dst_step, int dst_offset, - uchar4 src2, int rows, int cols, int dst_step1) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 2; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align (dst_offset & 3) - int src1_index = mad24(y, src1_step, x + src1_offset - dst_align); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + x & (int)0xfffffffc); - - uchar4 src1_data = vload4(0, src1 + src1_index); - uchar4 src2_data = (uchar4)(src2.x, src2.x, src2.x, src2.x); - - uchar4 data = *((__global uchar4 *)(dst + dst_index)); - uchar4 tmp_data = src1_data OP_BINARY src2_data; - - data.x = ((dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : data.x; - data.y = ((dst_index + 1 >= dst_start) && (dst_index + 1 < dst_end)) ? tmp_data.y : data.y; - data.z = ((dst_index + 2 >= dst_start) && (dst_index + 2 < dst_end)) ? tmp_data.z : data.z; - data.w = ((dst_index + 3 >= dst_start) && (dst_index + 3 < dst_end)) ? tmp_data.w : data.w; - - *((__global uchar4 *)(dst + dst_index)) = data; - } -} - - -__kernel void arithm_s_bitwise_binary_C1_D1 ( - __global char *src1, int src1_step, int src1_offset, - __global char *dst, int dst_step, int dst_offset, - char4 src2, int rows, int cols, int dst_step1) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 2; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align (dst_offset & 3) - int src1_index = mad24(y, src1_step, x + src1_offset - dst_align); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + x & (int)0xfffffffc); - - char4 src1_data = vload4(0, src1 + src1_index); - char4 src2_data = (char4)(src2.x, src2.x, src2.x, src2.x); - - char4 data = *((__global char4 *)(dst + dst_index)); - char4 tmp_data = src1_data OP_BINARY src2_data; - - data.x = ((dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : data.x; - data.y = ((dst_index + 1 >= dst_start) && (dst_index + 1 < dst_end)) ? tmp_data.y : data.y; - data.z = ((dst_index + 2 >= dst_start) && (dst_index + 2 < dst_end)) ? tmp_data.z : data.z; - data.w = ((dst_index + 3 >= dst_start) && (dst_index + 3 < dst_end)) ? tmp_data.w : data.w; - - *((__global char4 *)(dst + dst_index)) = data; - } -} - -__kernel void arithm_s_bitwise_binary_C1_D2 ( - __global ushort *src1, int src1_step, int src1_offset, - __global ushort *dst, int dst_step, int dst_offset, - ushort4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 1; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align ((dst_offset >> 1) & 1) - int src1_index = mad24(y, src1_step, (x << 1) + src1_offset - (dst_align << 1)); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + (x << 1) & (int)0xfffffffc); - - ushort2 src1_data = vload2(0, (__global ushort *)((__global char *)src1 + src1_index)); - ushort2 src2_data = (ushort2)(src2.x, src2.x); - - ushort2 data = *((__global ushort2 *)((__global uchar *)dst + dst_index)); - ushort2 tmp_data = src1_data OP_BINARY src2_data; - - data.x = (dst_index + 0 >= dst_start) ? tmp_data.x : data.x; - data.y = (dst_index + 2 < dst_end ) ? tmp_data.y : data.y; - - *((__global ushort2 *)((__global uchar *)dst + dst_index)) = data; - } -} -__kernel void arithm_s_bitwise_binary_C1_D3 ( - __global short *src1, int src1_step, int src1_offset, - __global short *dst, int dst_step, int dst_offset, - short4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - if (x < cols && y < rows) - { - x = x << 1; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align ((dst_offset >> 1) & 1) - int src1_index = mad24(y, src1_step, (x << 1) + src1_offset - (dst_align << 1)); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + (x << 1) & (int)0xfffffffc); - - short2 src1_data = vload2(0, (__global short *)((__global char *)src1 + src1_index)); - short2 src2_data = (short2)(src2.x, src2.x); - short2 data = *((__global short2 *)((__global uchar *)dst + dst_index)); - - short2 tmp_data = src1_data OP_BINARY src2_data; - - data.x = (dst_index + 0 >= dst_start) ? tmp_data.x : data.x; - data.y = (dst_index + 2 < dst_end ) ? tmp_data.y : data.y; - - *((__global short2 *)((__global uchar *)dst + dst_index)) = data; - } -} -__kernel void arithm_s_bitwise_binary_C1_D4 ( - __global int *src1, int src1_step, int src1_offset, - __global int *dst, int dst_step, int dst_offset, - int4 src2, int rows, int cols, int dst_step1) +__kernel void arithm_bitwise_binary_scalar( + __global uchar *src1, int src1_step, int src1_offset, + __global uchar *src2, int elemSize, + __global uchar *dst, int dst_step, int dst_offset, + int cols1, int rows) { - int x = get_global_id(0); int y = get_global_id(1); - if (x < cols && y < rows) + if (x < cols1 && y < rows) { - int src1_index = mad24(y, src1_step, (x << 2) + src1_offset); - int dst_index = mad24(y, dst_step, (x << 2) + dst_offset); - - int src_data1 = *((__global int *)((__global char *)src1 + src1_index)); - int src_data2 = src2.x; - - int data = src_data1 OP_BINARY src_data2; - - *((__global int *)((__global char *)dst + dst_index)) = data; - } -} -__kernel void arithm_s_bitwise_binary_C1_D5 ( - __global char *src1, int src1_step, int src1_offset, - __global char *dst, int dst_step, int dst_offset, - char16 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 2) + src1_offset); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, (x << 2) + dst_offset); - - char4 src1_data = *((__global char4 *)((__global char *)src1 + src1_index)); - char4 src2_data = (char4)(src2.s0, src2.s1, src2.s2, src2.s3); - - char4 data = *((__global char4 *)((__global char *)dst + dst_index)); - char4 tmp_data = src1_data OP_BINARY src2_data; - - data.x = ((dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : data.x; - data.y = ((dst_index + 1 >= dst_start) && (dst_index + 1 < dst_end)) ? tmp_data.y : data.y; - data.z = ((dst_index + 2 >= dst_start) && (dst_index + 2 < dst_end)) ? tmp_data.z : data.z; - data.w = ((dst_index + 3 >= dst_start) && (dst_index + 3 < dst_end)) ? tmp_data.w : data.w; - - *((__global char4 *)((__global char *)dst + dst_index)) = data; - } -} -#if defined (DOUBLE_SUPPORT) -__kernel void arithm_s_bitwise_binary_C1_D6 ( - __global short *src1, int src1_step, int src1_offset, - __global short *dst, int dst_step, int dst_offset, - short16 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 3) + src1_offset); - int dst_index = mad24(y, dst_step, (x << 3) + dst_offset); - - short4 src1_data = *((__global short4 *)((__global char *)src1 + src1_index)); - short4 src2_data = (short4)(src2.s0, src2.s1, src2.s2, src2.s3); - - short4 tmp_data = src1_data OP_BINARY src2_data; - - *((__global short4 *)((__global char *)dst + dst_index)) = tmp_data; - } -} -#endif -__kernel void arithm_s_bitwise_binary_C2_D0 ( - __global uchar *src1, int src1_step, int src1_offset, - __global uchar *dst, int dst_step, int dst_offset, - uchar4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 1; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align ((dst_offset >> 1) & 1) - int src1_index = mad24(y, src1_step, (x << 1) + src1_offset - (dst_align << 1)); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + (x << 1) & (int)0xfffffffc); - - uchar4 src1_data = vload4(0, src1 + src1_index); - uchar4 src2_data = (uchar4)(src2.x, src2.y, src2.x, src2.y); - - uchar4 data = *((__global uchar4 *)(dst + dst_index)); - uchar4 tmp_data = src1_data OP_BINARY src2_data; - - - data.xy = (dst_index + 0 >= dst_start) ? tmp_data.xy : data.xy; - data.zw = (dst_index + 2 < dst_end ) ? tmp_data.zw : data.zw; - - *((__global uchar4 *)(dst + dst_index)) = data; - } -} - - -__kernel void arithm_s_bitwise_binary_C2_D1 ( - __global char *src1, int src1_step, int src1_offset, - __global char *dst, int dst_step, int dst_offset, - char4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 1; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align ((dst_offset >> 1) & 1) - int src1_index = mad24(y, src1_step, (x << 1) + src1_offset - (dst_align << 1)); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + (x << 1) & (int)0xfffffffc); - - char4 src1_data = vload4(0, src1 + src1_index); - char4 src2_data = (char4)(src2.x, src2.y, src2.x, src2.y); - - char4 data = *((__global char4 *)(dst + dst_index)); - char4 tmp_data = src1_data OP_BINARY src2_data; - - data.xy = (dst_index + 0 >= dst_start) ? tmp_data.xy : data.xy; - data.zw = (dst_index + 2 < dst_end ) ? tmp_data.zw : data.zw; - - *((__global char4 *)(dst + dst_index)) = data; - } -} - -__kernel void arithm_s_bitwise_binary_C2_D2 ( - __global ushort *src1, int src1_step, int src1_offset, - __global ushort *dst, int dst_step, int dst_offset, - ushort4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 2) + src1_offset); - int dst_index = mad24(y, dst_step, (x << 2) + dst_offset); - - ushort2 src_data1 = *((__global ushort2 *)((__global char *)src1 + src1_index)); - ushort2 src_data2 = (ushort2)(src2.x, src2.y); - - ushort2 data = src_data1 OP_BINARY src_data2; - - *((__global ushort2 *)((__global char *)dst + dst_index)) = data; - } -} -__kernel void arithm_s_bitwise_binary_C2_D3 ( - __global short *src1, int src1_step, int src1_offset, - __global short *dst, int dst_step, int dst_offset, - short4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 2) + src1_offset); - int dst_index = mad24(y, dst_step, (x << 2) + dst_offset); - - short2 src_data1 = *((__global short2 *)((__global char *)src1 + src1_index)); - short2 src_data2 = (short2)(src2.x, src2.y); - - short2 data = src_data1 OP_BINARY src_data2; - - *((__global short2 *)((__global char *)dst + dst_index)) = data; - } -} -__kernel void arithm_s_bitwise_binary_C2_D4 ( - __global int *src1, int src1_step, int src1_offset, - __global int *dst, int dst_step, int dst_offset, - int4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 3) + src1_offset); - int dst_index = mad24(y, dst_step, (x << 3) + dst_offset); - - int2 src_data1 = *((__global int2 *)((__global char *)src1 + src1_index)); - int2 src_data2 = (int2)(src2.x, src2.y); - - int2 data = src_data1 OP_BINARY src_data2; - *((__global int2 *)((__global char *)dst + dst_index)) = data; - } -} -__kernel void arithm_s_bitwise_binary_C2_D5 ( - __global char *src1, int src1_step, int src1_offset, - __global char *dst, int dst_step, int dst_offset, - char16 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 3) + src1_offset); - int dst_index = mad24(y, dst_step, (x << 3) + dst_offset); - - char8 src1_data = *((__global char8 *)((__global char *)src1 + src1_index)); - char8 src2_data = (char8)(src2.s0, src2.s1, src2.s2, src2.s3, src2.s4, src2.s5, src2.s6, src2.s7); - - char8 tmp_data = src1_data OP_BINARY src2_data; - - *((__global char8 *)((__global char *)dst + dst_index)) = tmp_data; - } -} -#if defined (DOUBLE_SUPPORT) -__kernel void arithm_s_bitwise_binary_C2_D6 ( - __global short *src1, int src1_step, int src1_offset, - __global short *dst, int dst_step, int dst_offset, - short16 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 4) + src1_offset); - int dst_index = mad24(y, dst_step, (x << 4) + dst_offset); - - short8 src1_data = *((__global short8 *)((__global char *)src1 + src1_index)); - short8 src2_data = (short8)(src2.s0, src2.s1, src2.s2, src2.s3, src2.s4, src2.s5, src2.s6, src2.s7); - - short8 tmp_data = src1_data OP_BINARY src2_data; - - *((__global short8 *)((__global char *)dst + dst_index)) = tmp_data; - } -} -#endif - -__kernel void arithm_s_bitwise_binary_C4_D0 ( - __global uchar *src1, int src1_step, int src1_offset, - __global uchar *dst, int dst_step, int dst_offset, - uchar4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 2) + src1_offset); - int dst_index = mad24(y, dst_step, (x << 2) + dst_offset); - - uchar4 src_data1 = *((__global uchar4 *)(src1 + src1_index)); - - uchar4 data = src_data1 OP_BINARY src2; - - *((__global uchar4 *)(dst + dst_index)) = data; - } -} - - -__kernel void arithm_s_bitwise_binary_C4_D1 ( - __global char *src1, int src1_step, int src1_offset, - __global char *dst, int dst_step, int dst_offset, - char4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 2) + src1_offset); - int dst_index = mad24(y, dst_step, (x << 2) + dst_offset); - - char4 src_data1 = *((__global char4 *)(src1 + src1_index)); - - char4 data = src_data1 OP_BINARY src2; - - *((__global char4 *)(dst + dst_index)) = data; - } -} - -__kernel void arithm_s_bitwise_binary_C4_D2 ( - __global ushort *src1, int src1_step, int src1_offset, - __global ushort *dst, int dst_step, int dst_offset, - ushort4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 3) + src1_offset); - int dst_index = mad24(y, dst_step, (x << 3) + dst_offset); - - ushort4 src_data1 = *((__global ushort4 *)((__global char *)src1 + src1_index)); - - ushort4 data = src_data1 OP_BINARY src2; - - *((__global ushort4 *)((__global char *)dst + dst_index)) = data; - } -} -__kernel void arithm_s_bitwise_binary_C4_D3 ( - __global short *src1, int src1_step, int src1_offset, - __global short *dst, int dst_step, int dst_offset, - short4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 3) + src1_offset); - int dst_index = mad24(y, dst_step, (x << 3) + dst_offset); - - short4 src_data1 = *((__global short4 *)((__global char *)src1 + src1_index)); - - short4 data = src_data1 OP_BINARY src2; - - *((__global short4 *)((__global char *)dst + dst_index)) = data; - } -} -__kernel void arithm_s_bitwise_binary_C4_D4 ( - __global int *src1, int src1_step, int src1_offset, - __global int *dst, int dst_step, int dst_offset, - int4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 4) + src1_offset); - int dst_index = mad24(y, dst_step, (x << 4) + dst_offset); - - int4 src_data1 = *((__global int4 *)((__global char *)src1 + src1_index)); - - int4 data = src_data1 OP_BINARY src2; - - *((__global int4 *)((__global char *)dst + dst_index)) = data; - } -} -__kernel void arithm_s_bitwise_binary_C4_D5 ( - __global char *src1, int src1_step, int src1_offset, - __global char *dst, int dst_step, int dst_offset, - char16 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 4) + src1_offset); - int dst_index = mad24(y, dst_step, (x << 4) + dst_offset); - - char16 src1_data = *((__global char16 *)((__global char *)src1 + src1_index)); - char16 src2_data = (char16)(src2.s0, src2.s1, src2.s2, src2.s3, src2.s4, src2.s5, src2.s6, src2.s7, - src2.s8, src2.s9, src2.sa, src2.sb, src2.sc, src2.sd, src2.se, src2.sf); - - char16 tmp_data = src1_data OP_BINARY src2_data; - - *((__global char16 *)((__global char *)dst + dst_index)) = tmp_data; - } -} -#if defined (DOUBLE_SUPPORT) -__kernel void arithm_s_bitwise_binary_C4_D6 ( - __global short *src1, int src1_step, int src1_offset, - __global short *dst, int dst_step, int dst_offset, - short16 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 5) + src1_offset); - int dst_index = mad24(y, dst_step, (x << 5) + dst_offset); - - short4 src1_data_0 = *((__global short4 *)((__global char *)src1 + src1_index + 0)); - short4 src1_data_1 = *((__global short4 *)((__global char *)src1 + src1_index + 8)); - short4 src1_data_2 = *((__global short4 *)((__global char *)src1 + src1_index + 16)); - short4 src1_data_3 = *((__global short4 *)((__global char *)src1 + src1_index + 24)); - - short4 src2_data_0 = (short4)(src2.s0, src2.s1, src2.s2, src2.s3); - short4 src2_data_1 = (short4)(src2.s4, src2.s5, src2.s6, src2.s7); - short4 src2_data_2 = (short4)(src2.s8, src2.s9, src2.sa, src2.sb); - short4 src2_data_3 = (short4)(src2.sc, src2.sd, src2.se, src2.sf); - - short4 tmp_data_0 = src1_data_0 OP_BINARY src2_data_0; - short4 tmp_data_1 = src1_data_1 OP_BINARY src2_data_1; - short4 tmp_data_2 = src1_data_2 OP_BINARY src2_data_2; - short4 tmp_data_3 = src1_data_3 OP_BINARY src2_data_3; - - *((__global short4 *)((__global char *)dst + dst_index + 0 ))= tmp_data_0; - *((__global short4 *)((__global char *)dst + dst_index + 8 ))= tmp_data_1; - *((__global short4 *)((__global char *)dst + dst_index + 16))= tmp_data_2; - *((__global short4 *)((__global char *)dst + dst_index + 24))= tmp_data_3; + int src1_index = mad24(y, src1_step, src1_offset + x); + int src2_index = x % elemSize; + int dst_index = mad24(y, dst_step, dst_offset + x); + dst[dst_index] = src1[src1_index] Operation src2[src2_index]; } } -#endif diff --git a/modules/ocl/src/opencl/arithm_bitwise_binary_scalar_mask.cl b/modules/ocl/src/opencl/arithm_bitwise_binary_scalar_mask.cl index 9af6589adf..bae1699a3e 100644 --- a/modules/ocl/src/opencl/arithm_bitwise_binary_scalar_mask.cl +++ b/modules/ocl/src/opencl/arithm_bitwise_binary_scalar_mask.cl @@ -42,6 +42,7 @@ // the use of this software, even if advised of the possibility of such damage. // //M*/ + #if defined (DOUBLE_SUPPORT) #ifdef cl_khr_fp64 #pragma OPENCL EXTENSION cl_khr_fp64:enable @@ -50,698 +51,29 @@ #endif #endif -#ifndef OP_BINARY -#define OP_BINARY & -#endif - ////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////bitwise_binary//////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////// -/**************************************bitwise_binary with scalar with mask**************************************/ -__kernel void arithm_s_bitwise_binary_with_mask_C1_D0 ( - __global uchar *src1, int src1_step, int src1_offset, - __global uchar *dst, int dst_step, int dst_offset, - __global uchar *mask, int mask_step, int mask_offset, - uchar4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 2; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align (dst_offset & 3) - int src1_index = mad24(y, src1_step, x + src1_offset - dst_align); - int mask_index = mad24(y, mask_step, x + mask_offset - dst_align); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + x & (int)0xfffffffc); - - uchar4 src1_data = vload4(0, src1 + src1_index); - uchar4 src2_data = (uchar4)(src2.x, src2.x, src2.x, src2.x); - uchar4 mask_data = vload4(0, mask + mask_index); - - uchar4 data = *((__global uchar4 *)(dst + dst_index)); - uchar4 tmp_data = src1_data OP_BINARY src2_data; - - data.x = ((mask_data.x) && (dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : data.x; - data.y = ((mask_data.y) && (dst_index + 1 >= dst_start) && (dst_index + 1 < dst_end)) ? tmp_data.y : data.y; - data.z = ((mask_data.z) && (dst_index + 2 >= dst_start) && (dst_index + 2 < dst_end)) ? tmp_data.z : data.z; - data.w = ((mask_data.w) && (dst_index + 3 >= dst_start) && (dst_index + 3 < dst_end)) ? tmp_data.w : data.w; - - *((__global uchar4 *)(dst + dst_index)) = data; - } -} - - -__kernel void arithm_s_bitwise_binary_with_mask_C1_D1 ( - __global char *src1, int src1_step, int src1_offset, - __global char *dst, int dst_step, int dst_offset, - __global uchar *mask, int mask_step, int mask_offset, - char4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 2; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align (dst_offset & 3) - int src1_index = mad24(y, src1_step, x + src1_offset - dst_align); - int mask_index = mad24(y, mask_step, x + mask_offset - dst_align); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + x & (int)0xfffffffc); - - char4 src1_data = vload4(0, src1 + src1_index); - char4 src2_data = (char4)(src2.x, src2.x, src2.x, src2.x); - uchar4 mask_data = vload4(0, mask + mask_index); - - char4 data = *((__global char4 *)(dst + dst_index)); - char4 tmp_data = src1_data OP_BINARY src2_data; - - data.x = ((mask_data.x) && (dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : data.x; - data.y = ((mask_data.y) && (dst_index + 1 >= dst_start) && (dst_index + 1 < dst_end)) ? tmp_data.y : data.y; - data.z = ((mask_data.z) && (dst_index + 2 >= dst_start) && (dst_index + 2 < dst_end)) ? tmp_data.z : data.z; - data.w = ((mask_data.w) && (dst_index + 3 >= dst_start) && (dst_index + 3 < dst_end)) ? tmp_data.w : data.w; - - *((__global char4 *)(dst + dst_index)) = data; - } -} - -__kernel void arithm_s_bitwise_binary_with_mask_C1_D2 ( - __global ushort *src1, int src1_step, int src1_offset, - __global ushort *dst, int dst_step, int dst_offset, - __global uchar *mask, int mask_step, int mask_offset, - ushort4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 1; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align ((dst_offset / 2) & 1) - int src1_index = mad24(y, src1_step, (x << 1) + src1_offset - (dst_align << 1)); - int mask_index = mad24(y, mask_step, x + mask_offset - dst_align); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + (x << 1) & (int)0xfffffffc); - - ushort2 src1_data = vload2(0, (__global ushort *)((__global char *)src1 + src1_index)); - ushort2 src2_data = (ushort2)(src2.x, src2.x); - uchar2 mask_data = vload2(0, mask + mask_index); - - ushort2 data = *((__global ushort2 *)((__global uchar *)dst + dst_index)); - ushort2 tmp_data = src1_data OP_BINARY src2_data; - - data.x = ((mask_data.x) && (dst_index + 0 >= dst_start)) ? tmp_data.x : data.x; - data.y = ((mask_data.y) && (dst_index + 2 < dst_end )) ? tmp_data.y : data.y; - - *((__global ushort2 *)((__global uchar *)dst + dst_index)) = data; - } -} -__kernel void arithm_s_bitwise_binary_with_mask_C1_D3 ( - __global short *src1, int src1_step, int src1_offset, - __global short *dst, int dst_step, int dst_offset, - __global uchar *mask, int mask_step, int mask_offset, - short4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 1; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align ((dst_offset / 2) & 1) - int src1_index = mad24(y, src1_step, (x << 1) + src1_offset - (dst_align << 1)); - int mask_index = mad24(y, mask_step, x + mask_offset - dst_align); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + (x << 1) & (int)0xfffffffc); - - short2 src1_data = vload2(0, (__global short *)((__global char *)src1 + src1_index)); - short2 src2_data = (short2)(src2.x, src2.x); - uchar2 mask_data = vload2(0, mask + mask_index); - - short2 data = *((__global short2 *)((__global uchar *)dst + dst_index)); - short2 tmp_data = src1_data OP_BINARY src2_data; - - data.x = ((mask_data.x) && (dst_index + 0 >= dst_start)) ? tmp_data.x : data.x; - data.y = ((mask_data.y) && (dst_index + 2 < dst_end )) ? tmp_data.y : data.y; - - *((__global short2 *)((__global uchar *)dst + dst_index)) = data; - } -} -__kernel void arithm_s_bitwise_binary_with_mask_C1_D4 ( - __global int *src1, int src1_step, int src1_offset, - __global int *dst, int dst_step, int dst_offset, - __global uchar *mask, int mask_step, int mask_offset, - int4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 2) + src1_offset); - int mask_index = mad24(y, mask_step, x + mask_offset); - int dst_index = mad24(y, dst_step, (x << 2) + dst_offset); - - uchar mask_data = *(mask + mask_index); - - int src_data1 = *((__global int *)((__global char *)src1 + src1_index)); - int src_data2 = src2.x; - int dst_data = *((__global int *)((__global char *)dst + dst_index)); - - int data = src_data1 OP_BINARY src_data2; - data = mask_data ? data : dst_data; - - *((__global int *)((__global char *)dst + dst_index)) = data; - } -} - -__kernel void arithm_s_bitwise_binary_with_mask_C1_D5 ( - __global char *src1, int src1_step, int src1_offset, - __global char *dst, int dst_step, int dst_offset, - __global uchar *mask, int mask_step, int mask_offset, - char16 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 2) + src1_offset); - int mask_index = mad24(y, mask_step, x + mask_offset); - int dst_index = mad24(y, dst_step, (x << 2) + dst_offset); - - uchar mask_data = *(mask + mask_index); - - char4 src1_data = *((__global char4 *)((__global char *)src1 + src1_index)); - char4 src2_data = (char4)(src2.s0, src2.s1, src2.s2, src2.s3); - char4 dst_data = *((__global char4 *)((__global char *)dst + dst_index)); - - char4 data = src1_data OP_BINARY src2_data; - data = mask_data ? data : dst_data; - - *((__global char4 *)((__global char *)dst + dst_index)) = data; - } -} - -#if defined (DOUBLE_SUPPORT) -__kernel void arithm_s_bitwise_binary_with_mask_C1_D6 ( - __global short *src1, int src1_step, int src1_offset, - __global short *dst, int dst_step, int dst_offset, - __global uchar *mask, int mask_step, int mask_offset, - short16 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 3) + src1_offset); - int mask_index = mad24(y, mask_step, x + mask_offset); - int dst_index = mad24(y, dst_step, (x << 3) + dst_offset); - - uchar mask_data = *(mask + mask_index); - - short4 src1_data = *((__global short4 *)((__global char *)src1 + src1_index)); - short4 src2_data = (short4)(src2.s0, src2.s1, src2.s2, src2.s3); - short4 dst_data = *((__global short4 *)((__global char *)dst + dst_index)); - - short4 data = src1_data OP_BINARY src2_data; - data = mask_data ? data : dst_data; - - *((__global short4 *)((__global char *)dst + dst_index)) = data; - } -} -#endif -__kernel void arithm_s_bitwise_binary_with_mask_C2_D0 ( - __global uchar *src1, int src1_step, int src1_offset, - __global uchar *dst, int dst_step, int dst_offset, - __global uchar *mask, int mask_step, int mask_offset, - uchar4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 1; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align ((dst_offset / 2) & 1) - int src1_index = mad24(y, src1_step, (x << 1) + src1_offset - (dst_align << 1)); - int mask_index = mad24(y, mask_step, x + mask_offset - dst_align); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + (x << 1) & (int)0xfffffffc); - - uchar4 src1_data = vload4(0, src1 + src1_index); - uchar4 src2_data = (uchar4)(src2.x, src2.y, src2.x, src2.y); - uchar2 mask_data = vload2(0, mask + mask_index); - - uchar4 data = *((__global uchar4 *)(dst + dst_index)); - uchar4 tmp_data = src1_data OP_BINARY src2_data; - - data.xy = ((mask_data.x) && (dst_index + 0 >= dst_start)) ? tmp_data.xy : data.xy; - data.zw = ((mask_data.y) && (dst_index + 2 < dst_end )) ? tmp_data.zw : data.zw; - - *((__global uchar4 *)(dst + dst_index)) = data; - } -} - - -__kernel void arithm_s_bitwise_binary_with_mask_C2_D1 ( - __global char *src1, int src1_step, int src1_offset, - __global char *dst, int dst_step, int dst_offset, - __global uchar *mask, int mask_step, int mask_offset, - char4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 1; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align ((dst_offset / 2) & 1) - int src1_index = mad24(y, src1_step, (x << 1) + src1_offset - (dst_align << 1)); - int mask_index = mad24(y, mask_step, x + mask_offset - dst_align); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + (x << 1) & (int)0xfffffffc); - - char4 src1_data = vload4(0, src1 + src1_index); - char4 src2_data = (char4)(src2.x, src2.y, src2.x, src2.y); - uchar2 mask_data = vload2(0, mask + mask_index); - - char4 data = *((__global char4 *)(dst + dst_index)); - char4 tmp_data = src1_data OP_BINARY src2_data; - - data.xy = ((mask_data.x) && (dst_index + 0 >= dst_start)) ? tmp_data.xy : data.xy; - data.zw = ((mask_data.y) && (dst_index + 2 < dst_end )) ? tmp_data.zw : data.zw; - - *((__global char4 *)(dst + dst_index)) = data; - } -} - -__kernel void arithm_s_bitwise_binary_with_mask_C2_D2 ( - __global ushort *src1, int src1_step, int src1_offset, - __global ushort *dst, int dst_step, int dst_offset, - __global uchar *mask, int mask_step, int mask_offset, - ushort4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 2) + src1_offset); - int mask_index = mad24(y, mask_step, x + mask_offset); - int dst_index = mad24(y, dst_step, (x << 2) + dst_offset); - - uchar mask_data = *(mask + mask_index); - - ushort2 src_data1 = *((__global ushort2 *)((__global char *)src1 + src1_index)); - ushort2 src_data2 = (ushort2)(src2.x, src2.y); - ushort2 dst_data = *((__global ushort2 *)((__global char *)dst + dst_index)); - - ushort2 data = src_data1 OP_BINARY src_data2; - data = mask_data ? data : dst_data; - - *((__global ushort2 *)((__global char *)dst + dst_index)) = data; - } -} -__kernel void arithm_s_bitwise_binary_with_mask_C2_D3 ( - __global short *src1, int src1_step, int src1_offset, - __global short *dst, int dst_step, int dst_offset, - __global uchar *mask, int mask_step, int mask_offset, - short4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 2) + src1_offset); - int mask_index = mad24(y, mask_step, x + mask_offset); - int dst_index = mad24(y, dst_step, (x << 2) + dst_offset); - uchar mask_data = *(mask + mask_index); - - short2 src_data1 = *((__global short2 *)((__global char *)src1 + src1_index)); - short2 src_data2 = (short2)(src2.x, src2.y); - short2 dst_data = *((__global short2 *)((__global char *)dst + dst_index)); - - short2 data = src_data1 OP_BINARY src_data2; - data = mask_data ? data : dst_data; - - *((__global short2 *)((__global char *)dst + dst_index)) = data; - } -} -__kernel void arithm_s_bitwise_binary_with_mask_C2_D4 ( - __global int *src1, int src1_step, int src1_offset, - __global int *dst, int dst_step, int dst_offset, - __global uchar *mask, int mask_step, int mask_offset, - int4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 3) + src1_offset); - int mask_index = mad24(y, mask_step, x + mask_offset); - int dst_index = mad24(y, dst_step, (x << 3) + dst_offset); - - uchar mask_data = *(mask + mask_index); - - int2 src_data1 = *((__global int2 *)((__global char *)src1 + src1_index)); - int2 src_data2 = (int2)(src2.x, src2.y); - int2 dst_data = *((__global int2 *)((__global char *)dst + dst_index)); - - int2 data = src_data1 OP_BINARY src_data2; - data = mask_data ? data : dst_data; - - *((__global int2 *)((__global char *)dst + dst_index)) = data; - } -} -__kernel void arithm_s_bitwise_binary_with_mask_C2_D5 ( - __global char *src1, int src1_step, int src1_offset, - __global char *dst, int dst_step, int dst_offset, - __global uchar *mask, int mask_step, int mask_offset, - char16 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 3) + src1_offset); - int mask_index = mad24(y, mask_step, x + mask_offset); - int dst_index = mad24(y, dst_step, (x << 3) + dst_offset); - - uchar mask_data = *(mask + mask_index); - - char8 src1_data = *((__global char8 *)((__global char *)src1 + src1_index)); - char8 src2_data = (char8)(src2.s0, src2.s1, src2.s2, src2.s3, src2.s4, src2.s5, src2.s6, src2.s7); - char8 dst_data = *((__global char8 *)((__global char *)dst + dst_index)); - - char8 data = src1_data OP_BINARY src2_data; - - data = mask_data ? data : dst_data; - - *((__global char8 *)((__global char *)dst + dst_index)) = data; - } -} -#if defined (DOUBLE_SUPPORT) -__kernel void arithm_s_bitwise_binary_with_mask_C2_D6 ( - __global short *src1, int src1_step, int src1_offset, - __global short *dst, int dst_step, int dst_offset, +__kernel void arithm_bitwise_binary_scalar_mask(__global uchar *src1, int src1_step, int src1_offset, + __global uchar *src2, int elemSize, __global uchar *mask, int mask_step, int mask_offset, - short16 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 4) + src1_offset); - int mask_index = mad24(y, mask_step, x + mask_offset); - int dst_index = mad24(y, dst_step, (x << 4) + dst_offset); - - uchar mask_data = *(mask + mask_index); - - short8 src1_data = *((__global short8 *)((__global char *)src1 + src1_index)); - short8 src2_data = (short8)(src2.s0, src2.s1, src2.s2, src2.s3, src2.s4, src2.s5, src2.s6, src2.s7); - short8 dst_data = *((__global short8 *)((__global char *)dst + dst_index)); - - short8 data = src1_data OP_BINARY src2_data; - data = mask_data ? data : dst_data; - - *((__global short8 *)((__global char *)dst + dst_index)) = data; - } -} -#endif -__kernel void arithm_s_bitwise_binary_with_mask_C4_D0 ( - __global uchar *src1, int src1_step, int src1_offset, - __global uchar *dst, int dst_step, int dst_offset, - __global uchar *mask, int mask_step, int mask_offset, - uchar4 src2, int rows, int cols, int dst_step1) + __global uchar *dst, int dst_step, int dst_offset, + int cols, int rows) { - int x = get_global_id(0); int y = get_global_id(1); if (x < cols && y < rows) { - int src1_index = mad24(y, src1_step, (x << 2) + src1_offset); - int mask_index = mad24(y, mask_step, x + mask_offset); - int dst_index = mad24(y, dst_step, (x << 2) + dst_offset); + int mask_index = mad24(y, mask_step, (x / elemSize) + mask_offset); + if (mask[mask_index]) + { + int src1_index = mad24(y, src1_step, x + src1_offset); + int src2_index = x % elemSize; + int dst_index = mad24(y, dst_step, x + dst_offset); - uchar mask_data = *(mask + mask_index); - - uchar4 src_data1 = *((__global uchar4 *)(src1 + src1_index)); - uchar4 dst_data = *((__global uchar4 *)(dst + dst_index)); - - uchar4 data = src_data1 OP_BINARY src2; - data = mask_data ? data : dst_data; - - *((__global uchar4 *)(dst + dst_index)) = data; + dst[dst_index] = src1[src1_index] Operation src2[src2_index]; + } } } - - -__kernel void arithm_s_bitwise_binary_with_mask_C4_D1 ( - __global char *src1, int src1_step, int src1_offset, - __global char *dst, int dst_step, int dst_offset, - __global uchar *mask, int mask_step, int mask_offset, - char4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 2) + src1_offset); - int mask_index = mad24(y, mask_step, x + mask_offset); - int dst_index = mad24(y, dst_step, (x << 2) + dst_offset); - - uchar mask_data = *(mask + mask_index); - - char4 src_data1 = *((__global char4 *)(src1 + src1_index)); - char4 dst_data = *((__global char4 *)(dst + dst_index)); - - char4 data = src_data1 OP_BINARY src2; - data = mask_data ? data : dst_data; - - *((__global char4 *)(dst + dst_index)) = data; - } -} - -__kernel void arithm_s_bitwise_binary_with_mask_C4_D2 ( - __global ushort *src1, int src1_step, int src1_offset, - __global ushort *dst, int dst_step, int dst_offset, - __global uchar *mask, int mask_step, int mask_offset, - ushort4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 3) + src1_offset); - int mask_index = mad24(y, mask_step, x + mask_offset); - int dst_index = mad24(y, dst_step, (x << 3) + dst_offset); - - uchar mask_data = *(mask + mask_index); - - ushort4 src_data1 = *((__global ushort4 *)((__global char *)src1 + src1_index)); - ushort4 dst_data = *((__global ushort4 *)((__global char *)dst + dst_index)); - - ushort4 data = src_data1 OP_BINARY src2; - data = mask_data ? data : dst_data; - - *((__global ushort4 *)((__global char *)dst + dst_index)) = data; - } -} -__kernel void arithm_s_bitwise_binary_with_mask_C4_D3 ( - __global short *src1, int src1_step, int src1_offset, - __global short *dst, int dst_step, int dst_offset, - __global uchar *mask, int mask_step, int mask_offset, - short4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 3) + src1_offset); - int mask_index = mad24(y, mask_step, x + mask_offset); - int dst_index = mad24(y, dst_step, (x << 3) + dst_offset); - - uchar mask_data = *(mask + mask_index); - - short4 src_data1 = *((__global short4 *)((__global char *)src1 + src1_index)); - short4 dst_data = *((__global short4 *)((__global char *)dst + dst_index)); - - short4 data = src_data1 OP_BINARY src2; - data = mask_data ? data : dst_data; - - *((__global short4 *)((__global char *)dst + dst_index)) = data; - } -} -__kernel void arithm_s_bitwise_binary_with_mask_C4_D4 ( - __global int *src1, int src1_step, int src1_offset, - __global int *dst, int dst_step, int dst_offset, - __global uchar *mask, int mask_step, int mask_offset, - int4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 4) + src1_offset); - int mask_index = mad24(y, mask_step, x + mask_offset); - int dst_index = mad24(y, dst_step, (x << 4) + dst_offset); - - uchar mask_data = *(mask + mask_index); - - int4 src_data1 = *((__global int4 *)((__global char *)src1 + src1_index)); - int4 dst_data = *((__global int4 *)((__global char *)dst + dst_index)); - - int4 data = src_data1 OP_BINARY src2; - data = mask_data ? data : dst_data; - - *((__global int4 *)((__global char *)dst + dst_index)) = data; - } -} -__kernel void arithm_s_bitwise_binary_with_mask_C4_D5 ( - __global char *src1, int src1_step, int src1_offset, - __global char *dst, int dst_step, int dst_offset, - __global uchar *mask, int mask_step, int mask_offset, - char16 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 4) + src1_offset); - int mask_index = mad24(y, mask_step, x + mask_offset); - int dst_index = mad24(y, dst_step, (x << 4) + dst_offset); - - uchar mask_data = *(mask + mask_index); - - char16 src1_data = *((__global char16 *)((__global char *)src1 + src1_index)); - char16 src2_data = (char16)(src2.s0, src2.s1, src2.s2, src2.s3, src2.s4, src2.s5, src2.s6, src2.s7, - src2.s8, src2.s9, src2.sa, src2.sb, src2.sc, src2.sd, src2.se, src2.sf); - char16 dst_data = *((__global char16 *)((__global char *)dst + dst_index)); - - char16 data = src1_data OP_BINARY src2_data; - data = mask_data ? data : dst_data; - - *((__global char16 *)((__global char *)dst + dst_index)) = data; - } -} -#if defined (DOUBLE_SUPPORT) -__kernel void arithm_s_bitwise_binary_with_mask_C4_D6 ( - __global short *src1, int src1_step, int src1_offset, - __global short *dst, int dst_step, int dst_offset, - __global uchar *mask, int mask_step, int mask_offset, - short16 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 5) + src1_offset); - int mask_index = mad24(y, mask_step, x + mask_offset); - int dst_index = mad24(y, dst_step, (x << 5) + dst_offset); - - uchar mask_data = *(mask + mask_index); - - short4 src1_data_0 = *((__global short4 *)((__global char *)src1 + src1_index + 0)); - short4 src1_data_1 = *((__global short4 *)((__global char *)src1 + src1_index + 8)); - short4 src1_data_2 = *((__global short4 *)((__global char *)src1 + src1_index + 16)); - short4 src1_data_3 = *((__global short4 *)((__global char *)src1 + src1_index + 24)); - - short4 src2_data_0 = (short4)(src2.s0, src2.s1, src2.s2, src2.s3); - short4 src2_data_1 = (short4)(src2.s4, src2.s5, src2.s6, src2.s7); - short4 src2_data_2 = (short4)(src2.s8, src2.s9, src2.sa, src2.sb); - short4 src2_data_3 = (short4)(src2.sc, src2.sd, src2.se, src2.sf); - - short4 dst_data_0 = *((__global short4 *)((__global char *)dst + dst_index + 0)); - short4 dst_data_1 = *((__global short4 *)((__global char *)dst + dst_index + 8)); - short4 dst_data_2 = *((__global short4 *)((__global char *)dst + dst_index + 16)); - short4 dst_data_3 = *((__global short4 *)((__global char *)dst + dst_index + 24)); - - short4 data_0 = src1_data_0 OP_BINARY src2_data_0; - short4 data_1 = src1_data_1 OP_BINARY src2_data_1; - short4 data_2 = src1_data_2 OP_BINARY src2_data_2; - short4 data_3 = src1_data_3 OP_BINARY src2_data_3; - - data_0 = mask_data ? data_0 : dst_data_0; - data_1 = mask_data ? data_1 : dst_data_1; - data_2 = mask_data ? data_2 : dst_data_2; - data_3 = mask_data ? data_3 : dst_data_3; - - *((__global short4 *)((__global char *)dst + dst_index + 0)) = data_0; - *((__global short4 *)((__global char *)dst + dst_index + 8)) = data_1; - *((__global short4 *)((__global char *)dst + dst_index + 16)) = data_2; - *((__global short4 *)((__global char *)dst + dst_index + 24)) = data_3; - } -} -#endif From 073096357657ceb251303dd5162f4a7eecf48846 Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Tue, 24 Sep 2013 13:58:18 +0400 Subject: [PATCH 45/68] refactored and extended ocl::compare --- modules/ocl/src/arithm.cpp | 86 +- modules/ocl/src/opencl/arithm_compare.cl | 74 ++ modules/ocl/src/opencl/arithm_compare_eq.cl | 1016 ------------------- modules/ocl/src/opencl/arithm_compare_ne.cl | 1013 ------------------ 4 files changed, 106 insertions(+), 2083 deletions(-) create mode 100644 modules/ocl/src/opencl/arithm_compare.cl delete mode 100644 modules/ocl/src/opencl/arithm_compare_eq.cl delete mode 100644 modules/ocl/src/opencl/arithm_compare_ne.cl diff --git a/modules/ocl/src/arithm.cpp b/modules/ocl/src/arithm.cpp index 4f6737e822..1effac2138 100644 --- a/modules/ocl/src/arithm.cpp +++ b/modules/ocl/src/arithm.cpp @@ -82,8 +82,7 @@ namespace cv extern const char *arithm_bitwise_binary_scalar; extern const char *arithm_bitwise_binary_scalar_mask; extern const char *arithm_bitwise_not; - extern const char *arithm_compare_eq; - extern const char *arithm_compare_ne; + extern const char *arithm_compare; extern const char *arithm_transpose; extern const char *arithm_flip; extern const char *arithm_flip_rc; @@ -268,76 +267,55 @@ void cv::ocl::absdiff(const oclMat &src1, const Scalar &src2, oclMat &dst) ////////////////////////////////////////////////////////////////////////////// ///////////////////////////////// compare /////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// -static void compare_run(const oclMat &src1, const oclMat &src2, oclMat &dst, string kernelName, const char **kernelString) + +static void compare_run(const oclMat &src1, const oclMat &src2, oclMat &dst, int cmpOp, + string kernelName, const char **kernelString) { - dst.create(src1.size(), CV_8UC1); - CV_Assert(src1.oclchannels() == 1); CV_Assert(src1.type() == src2.type()); - Context *clCxt = src1.clCxt; + dst.create(src1.size(), CV_8UC1); + Context *clCxt = src1.clCxt; + int depth = src1.depth(); - int vector_lengths[7] = {4, 0, 4, 4, 4, 4, 4}; - size_t vector_length = vector_lengths[depth]; - int offset_cols = (dst.offset / dst.elemSize1()) & (vector_length - 1); - int cols = divUp(dst.cols + offset_cols, vector_length); size_t localThreads[3] = { 64, 4, 1 }; - size_t globalThreads[3] = { cols, dst.rows, 1 }; + size_t globalThreads[3] = { dst.cols, dst.rows, 1 }; + + int src1step1 = src1.step1(), src1offset1 = src1.offset / src1.elemSize1(); + int src2step1 = src2.step1(), src2offset1 = src2.offset / src2.elemSize1(); + int dststep1 = dst.step1(), dstoffset1 = dst.offset / dst.elemSize1(); + + const char * const typeMap[] = { "uchar", "char", "ushort", "short", "int", "float", "double" }; + const char * operationMap[] = { "==", ">", ">=", "<", "<=", "!=" }; + std::string buildOptions = format("-D T=%s -D Operation=%s", typeMap[depth], operationMap[cmpOp]); - int dst_step1 = dst.cols * dst.elemSize(); vector > args; args.push_back( make_pair( sizeof(cl_mem), (void *)&src1.data )); - args.push_back( make_pair( sizeof(cl_int), (void *)&src1.step )); - args.push_back( make_pair( sizeof(cl_int), (void *)&src1.offset )); + args.push_back( make_pair( sizeof(cl_int), (void *)&src1step1 )); + args.push_back( make_pair( sizeof(cl_int), (void *)&src1offset1 )); args.push_back( make_pair( sizeof(cl_mem), (void *)&src2.data )); - args.push_back( make_pair( sizeof(cl_int), (void *)&src2.step )); - args.push_back( make_pair( sizeof(cl_int), (void *)&src2.offset )); + args.push_back( make_pair( sizeof(cl_int), (void *)&src2step1 )); + args.push_back( make_pair( sizeof(cl_int), (void *)&src2offset1 )); args.push_back( make_pair( sizeof(cl_mem), (void *)&dst.data )); - args.push_back( make_pair( sizeof(cl_int), (void *)&dst.step )); - args.push_back( make_pair( sizeof(cl_int), (void *)&dst.offset )); + args.push_back( make_pair( sizeof(cl_int), (void *)&dststep1 )); + args.push_back( make_pair( sizeof(cl_int), (void *)&dstoffset1 )); + args.push_back( make_pair( sizeof(cl_int), (void *)&src1.cols )); args.push_back( make_pair( sizeof(cl_int), (void *)&src1.rows )); - args.push_back( make_pair( sizeof(cl_int), (void *)&cols )); - args.push_back( make_pair( sizeof(cl_int), (void *)&dst_step1 )); - openCLExecuteKernel(clCxt, kernelString, kernelName, globalThreads, localThreads, args, -1, depth); + + openCLExecuteKernel(clCxt, kernelString, kernelName, globalThreads, localThreads, + args, -1, -1, buildOptions.c_str()); } void cv::ocl::compare(const oclMat &src1, const oclMat &src2, oclMat &dst , int cmpOp) { - if(!src1.clCxt->supportsFeature(Context::CL_DOUBLE) && src1.type() == CV_64F) + if (!src1.clCxt->supportsFeature(Context::CL_DOUBLE) && src1.depth() == CV_64F) { cout << "Selected device do not support double" << endl; return; } - string kernelName; - const char **kernelString = NULL; - switch( cmpOp ) - { - case CMP_EQ: - kernelName = "arithm_compare_eq"; - kernelString = &arithm_compare_eq; - break; - case CMP_GT: - kernelName = "arithm_compare_gt"; - kernelString = &arithm_compare_eq; - break; - case CMP_GE: - kernelName = "arithm_compare_ge"; - kernelString = &arithm_compare_eq; - break; - case CMP_NE: - kernelName = "arithm_compare_ne"; - kernelString = &arithm_compare_ne; - break; - case CMP_LT: - kernelName = "arithm_compare_lt"; - kernelString = &arithm_compare_ne; - break; - case CMP_LE: - kernelName = "arithm_compare_le"; - kernelString = &arithm_compare_ne; - break; - default: - CV_Error(CV_StsBadArg, "Unknown comparison method"); - } - compare_run(src1, src2, dst, kernelName, kernelString); + + CV_Assert(src1.channels() == 1 && src2.channels() == 1); + CV_Assert(cmpOp >= CMP_EQ && cmpOp <= CMP_NE); + + compare_run(src1, src2, dst, cmpOp, "arithm_compare", &arithm_compare); } ////////////////////////////////////////////////////////////////////////////// diff --git a/modules/ocl/src/opencl/arithm_compare.cl b/modules/ocl/src/opencl/arithm_compare.cl new file mode 100644 index 0000000000..d0842db180 --- /dev/null +++ b/modules/ocl/src/opencl/arithm_compare.cl @@ -0,0 +1,74 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2010-2012, Institute Of Software Chinese Academy Of Science, all rights reserved. +// Copyright (C) 2010-2012, Advanced Micro Devices, Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// @Authors +// Jia Haipeng, jiahaipeng95@gmail.com +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other oclMaterials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors as is and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#if defined (DOUBLE_SUPPORT) +#ifdef cl_khr_fp64 +#pragma OPENCL EXTENSION cl_khr_fp64:enable +#elif defined (cl_amd_fp64) +#pragma OPENCL EXTENSION cl_amd_fp64:enable +#endif +#endif + +////////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////addWeighted////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////////// + +__kernel void arithm_compare(__global T * src1, int src1_step1, int src1_offset1, + __global T * src2, int src2_step1, int src2_offset1, + __global uchar * dst, int dst_step1, int dst_offset1, + int cols1, int rows) +{ + int x = get_global_id(0); + int y = get_global_id(1); + + if (x < cols1 && y < rows) + { + int src1_index = mad24(y, src1_step1, x + src1_offset1); + int src2_index = mad24(y, src2_step1, x + src2_offset1); + int dst_index = mad24(y, dst_step1, x + dst_offset1); + + dst[dst_index] = convert_uchar(src1[src1_index] Operation src2[src2_index] ? 255 : 0); + } +} diff --git a/modules/ocl/src/opencl/arithm_compare_eq.cl b/modules/ocl/src/opencl/arithm_compare_eq.cl deleted file mode 100644 index 16a56acef3..0000000000 --- a/modules/ocl/src/opencl/arithm_compare_eq.cl +++ /dev/null @@ -1,1016 +0,0 @@ -/*M/////////////////////////////////////////////////////////////////////////////////////// -// -// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. -// -// By downloading, copying, installing or using the software you agree to this license. -// If you do not agree to this license, do not download, install, -// copy or use the software. -// -// -// License Agreement -// For Open Source Computer Vision Library -// -// Copyright (C) 2010-2012, Institute Of Software Chinese Academy Of Science, all rights reserved. -// Copyright (C) 2010-2012, Advanced Micro Devices, Inc., all rights reserved. -// Third party copyrights are property of their respective owners. -// -// @Authors -// Jiang Liyuan, jlyuan001.good@163.com -// -// Redistribution and use in source and binary forms, with or without modification, -// are permitted provided that the following conditions are met: -// -// * Redistribution's of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// * Redistribution's in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other oclMaterials provided with the distribution. -// -// * The name of the copyright holders may not be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// This software is provided by the copyright holders and contributors as is and -// any express or implied warranties, including, but not limited to, the implied -// warranties of merchantability and fitness for a particular purpose are disclaimed. -// In no event shall the Intel Corporation or contributors be liable for any direct, -// indirect, incidental, special, exemplary, or consequential damages -// (including, but not limited to, procurement of substitute goods or services; -// loss of use, data, or profits; or business interruption) however caused -// and on any theory of liability, whether in contract, strict liability, -// or tort (including negligence or otherwise) arising in any way out of -// the use of this software, even if advised of the possibility of such damage. -// -//M*/ -#if defined (DOUBLE_SUPPORT) -#ifdef cl_khr_fp64 -#pragma OPENCL EXTENSION cl_khr_fp64:enable -#elif defined (cl_amd_fp64) -#pragma OPENCL EXTENSION cl_amd_fp64:enable -#endif -#endif - -////////////////////////////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////Compare EQ//////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////////////////////////////// - -__kernel void arithm_compare_eq_D0 (__global uchar *src1, int src1_step, int src1_offset, - __global uchar *src2, int src2_step, int src2_offset, - __global uchar *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 2; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align (dst_offset & 3) - int src1_index = mad24(y, src1_step, x + src1_offset - dst_align); - int src2_index = mad24(y, src2_step, x + src2_offset - dst_align); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + x & (int)0xfffffffc); - int src1_index_fix = src1_index < 0 ? 0 : src1_index; - int src2_index_fix = src2_index < 0 ? 0 : src2_index; - uchar4 src1_data = vload4(0, src1 + src1_index_fix); - uchar4 src2_data = vload4(0, src2 + src2_index_fix); - if(src1_index < 0) - { - uchar4 tmp; - tmp.xyzw = (src1_index == -2) ? src1_data.zwxy:src1_data.yzwx; - src1_data.xyzw = (src1_index == -1) ? src1_data.wxyz:tmp.xyzw; - } - if(src2_index < 0) - { - uchar4 tmp; - tmp.xyzw = (src2_index == -2) ? src2_data.zwxy:src2_data.yzwx; - src2_data.xyzw = (src2_index == -1) ? src2_data.wxyz:tmp.xyzw; - } - - - - uchar4 dst_data = *((__global uchar4 *)(dst + dst_index)); - uchar4 tmp_data = convert_uchar4((src1_data == src2_data)); - - dst_data.x = ((dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : dst_data.x; - dst_data.y = ((dst_index + 1 >= dst_start) && (dst_index + 1 < dst_end)) ? tmp_data.y : dst_data.y; - dst_data.z = ((dst_index + 2 >= dst_start) && (dst_index + 2 < dst_end)) ? tmp_data.z : dst_data.z; - dst_data.w = ((dst_index + 3 >= dst_start) && (dst_index + 3 < dst_end)) ? tmp_data.w : dst_data.w; - - *((__global uchar4 *)(dst + dst_index)) = dst_data; - } -} - - -__kernel void arithm_compare_ne_D2 (__global ushort *src1, int src1_step, int src1_offset, - __global ushort *src2, int src2_step, int src2_offset, - __global uchar *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) - -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 2; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align ((dst_offset >> 1)& 3) - int src1_index = mad24(y, src1_step, (x << 1) + src1_offset - (dst_align << 1)); - int src2_index = mad24(y, src2_step, (x << 1) + src2_offset - (dst_align << 1)); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + x & (int)0xfffffffc); - - int src1_index_fix = src1_index < 0 ? 0 : src1_index; - int src2_index_fix = src2_index < 0 ? 0 : src2_index; - ushort4 src1_data = vload4(0, (__global ushort *)((__global char *)src1 + src1_index)); - ushort4 src2_data = vload4(0, (__global ushort *)((__global char *)src2 + src2_index)); - if(src1_index < 0) - { - ushort4 tmp; - tmp.xyzw = (src1_index == -2) ? src1_data.zwxy:src1_data.yzwx; - src1_data.xyzw = (src1_index == -1) ? src1_data.wxyz:tmp.xyzw; - } - if(src2_index < 0) - { - ushort4 tmp; - tmp.xyzw = (src2_index == -2) ? src2_data.zwxy:src2_data.yzwx; - src2_data.xyzw = (src2_index == -1) ? src2_data.wxyz:tmp.xyzw; - } - - uchar4 dst_data = *((__global uchar4 *)(dst + dst_index)); - uchar4 tmp_data = convert_uchar4((src1_data == src2_data)); - - dst_data.x = ((dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : dst_data.x; - dst_data.y = ((dst_index + 1 >= dst_start) && (dst_index + 1 < dst_end)) ? tmp_data.y : dst_data.y; - dst_data.z = ((dst_index + 2 >= dst_start) && (dst_index + 2 < dst_end)) ? tmp_data.z : dst_data.z; - dst_data.w = ((dst_index + 3 >= dst_start) && (dst_index + 3 < dst_end)) ? tmp_data.w : dst_data.w; - - *((__global uchar4 *)(dst + dst_index)) = dst_data; - } -} - - -__kernel void arithm_compare_eq_D3 (__global short *src1, int src1_step, int src1_offset, - __global short *src2, int src2_step, int src2_offset, - __global uchar *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) - -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 2; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align ((dst_offset >> 1) & 3) - int src1_index = mad24(y, src1_step, (x << 1) + src1_offset - (dst_align << 1)); - int src2_index = mad24(y, src2_step, (x << 1) + src2_offset - (dst_align << 1)); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + x & (int)0xfffffffc); - int src1_index_fix = src1_index < 0 ? 0 : src1_index; - int src2_index_fix = src2_index < 0 ? 0 : src2_index; - short4 src1_data = vload4(0, (__global short *)((__global char *)src1 + src1_index)); - short4 src2_data = vload4(0, (__global short *)((__global char *)src2 + src2_index)); - if(src1_index < 0) - { - short4 tmp; - tmp.xyzw = (src1_index == -2) ? src1_data.zwxy:src1_data.yzwx; - src1_data.xyzw = (src1_index == -1) ? src1_data.wxyz:tmp.xyzw; - } - if(src2_index < 0) - { - short4 tmp; - tmp.xyzw = (src2_index == -2) ? src2_data.zwxy:src2_data.yzwx; - src2_data.xyzw = (src2_index == -1) ? src2_data.wxyz:tmp.xyzw; - } - - - - - uchar4 dst_data = *((__global uchar4 *)(dst + dst_index)); - uchar4 tmp_data = convert_uchar4((src1_data == src2_data)); - - dst_data.x = ((dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : dst_data.x; - dst_data.y = ((dst_index + 1 >= dst_start) && (dst_index + 1 < dst_end)) ? tmp_data.y : dst_data.y; - dst_data.z = ((dst_index + 2 >= dst_start) && (dst_index + 2 < dst_end)) ? tmp_data.z : dst_data.z; - dst_data.w = ((dst_index + 3 >= dst_start) && (dst_index + 3 < dst_end)) ? tmp_data.w : dst_data.w; - - *((__global uchar4 *)(dst + dst_index)) = dst_data; - } -} - - - -__kernel void arithm_compare_eq_D4 (__global int *src1, int src1_step, int src1_offset, - __global int *src2, int src2_step, int src2_offset, - __global uchar *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 2; -#ifdef dst_align -#undef dst_align -#endif -#define dst_align ((dst_offset >> 2) & 3) - int src1_index = mad24(y, src1_step, (x << 2) + src1_offset - (dst_align << 2)); - int src2_index = mad24(y, src2_step, (x << 2) + src2_offset - (dst_align << 2)); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + x & (int)0xfffffffc); - int src1_index_fix = src1_index < 0 ? 0 : src1_index; - int src2_index_fix = src2_index < 0 ? 0 : src2_index; - - int4 src1_data = vload4(0, (__global int *)((__global char *)src1 + src1_index)); - int4 src2_data = vload4(0, (__global int *)((__global char *)src2 + src2_index)); - if(src1_index < 0) - { - int4 tmp; - tmp.xyzw = (src1_index == -2) ? src1_data.zwxy:src1_data.yzwx; - src1_data.xyzw = (src1_index == -1) ? src1_data.wxyz:tmp.xyzw; - } - if(src2_index < 0) - { - int4 tmp; - tmp.xyzw = (src2_index == -2) ? src2_data.zwxy:src2_data.yzwx; - src2_data.xyzw = (src2_index == -1) ? src2_data.wxyz:tmp.xyzw; - } - - uchar4 dst_data = *((__global uchar4 *)(dst + dst_index)); - uchar4 tmp_data = convert_uchar4((src1_data == src2_data)); - - dst_data.x = ((dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : dst_data.x; - dst_data.y = ((dst_index + 1 >= dst_start) && (dst_index + 1 < dst_end)) ? tmp_data.y : dst_data.y; - dst_data.z = ((dst_index + 2 >= dst_start) && (dst_index + 2 < dst_end)) ? tmp_data.z : dst_data.z; - dst_data.w = ((dst_index + 3 >= dst_start) && (dst_index + 3 < dst_end)) ? tmp_data.w : dst_data.w; - - *((__global uchar4 *)(dst + dst_index)) = dst_data; - } -} - -__kernel void arithm_compare_eq_D5 (__global float *src1, int src1_step, int src1_offset, - __global float *src2, int src2_step, int src2_offset, - __global uchar *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 2; -#ifdef dst_align -#undef dst_align -#endif -#define dst_align ((dst_offset >> 2) & 3) - int src1_index = mad24(y, src1_step, (x << 2) + src1_offset - (dst_align << 2)); - int src2_index = mad24(y, src2_step, (x << 2) + src2_offset - (dst_align << 2)); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + x & (int)0xfffffffc); - int src1_index_fix = src1_index < 0 ? 0 : src1_index; - int src2_index_fix = src2_index < 0 ? 0 : src2_index; - float4 src1_data = vload4(0, (__global float *)((__global char *)src1 + src1_index_fix)); - float4 src2_data = vload4(0, (__global float *)((__global char *)src2 + src2_index_fix)); - if(src2_index < 0) - { - float4 tmp; - tmp.xyzw = (src2_index == -2) ? src2_data.zwxy:src2_data.yzwx; - src2_data.xyzw = (src2_index == -1) ? src2_data.wxyz:tmp.xyzw; - } - - - uchar4 dst_data = *((__global uchar4 *)(dst + dst_index)); - uchar4 tmp_data = convert_uchar4((src1_data == src2_data)); - - dst_data.x = ((dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : dst_data.x; - dst_data.y = ((dst_index + 1 >= dst_start) && (dst_index + 1 < dst_end)) ? tmp_data.y : dst_data.y; - dst_data.z = ((dst_index + 2 >= dst_start) && (dst_index + 2 < dst_end)) ? tmp_data.z : dst_data.z; - dst_data.w = ((dst_index + 3 >= dst_start) && (dst_index + 3 < dst_end)) ? tmp_data.w : dst_data.w; - - *((__global uchar4 *)(dst + dst_index)) = dst_data; - } -} - -#if defined (DOUBLE_SUPPORT) -__kernel void arithm_compare_eq_D6 (__global double *src1, int src1_step, int src1_offset, - __global double *src2, int src2_step, int src2_offset, - __global uchar *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 2; -#ifdef dst_align -#undef dst_align -#endif -#define dst_align ((dst_offset >> 3) & 3) - int src1_index = mad24(y, src1_step, (x << 3) + src1_offset - (dst_align << 3)); - int src2_index = mad24(y, src2_step, (x << 3) + src2_offset - (dst_align << 3)); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + x & (int)0xfffffffc); - int src1_index_fix = src1_index < 0 ? 0 : src1_index; - int src2_index_fix = src2_index < 0 ? 0 : src2_index; - double4 src1_data = vload4(0, (__global double *)((__global char *)src1 + src1_index_fix)); - double4 src2_data = vload4(0, (__global double *)((__global char *)src2 + src2_index_fix)); - if(src1_index < 0) - { - double4 tmp; - tmp.xyzw = (src1_index == -2) ? src1_data.zwxy:src1_data.yzwx; - src1_data.xyzw = (src1_index == -1) ? src1_data.wxyz:tmp.xyzw; - } - if(src2_index < 0) - { - double4 tmp; - tmp.xyzw = (src2_index == -2) ? src2_data.zwxy:src2_data.yzwx; - src2_data.xyzw = (src2_index == -1) ? src2_data.wxyz:tmp.xyzw; - } - - - uchar4 dst_data = *((__global uchar4 *)(dst + dst_index)); - uchar4 tmp_data = convert_uchar4((src1_data == src2_data)); - - dst_data.x = ((dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : dst_data.x; - dst_data.y = ((dst_index + 1 >= dst_start) && (dst_index + 1 < dst_end)) ? tmp_data.y : dst_data.y; - dst_data.z = ((dst_index + 2 >= dst_start) && (dst_index + 2 < dst_end)) ? tmp_data.z : dst_data.z; - dst_data.w = ((dst_index + 3 >= dst_start) && (dst_index + 3 < dst_end)) ? tmp_data.w : dst_data.w; - - *((__global uchar4 *)(dst + dst_index)) = dst_data; - } -} -#endif - -/***********************************Compare GT**************************/ -__kernel void arithm_compare_gt_D0 (__global uchar *src1, int src1_step, int src1_offset, - __global uchar *src2, int src2_step, int src2_offset, - __global uchar *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 2; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align (dst_offset & 3) - int src1_index = mad24(y, src1_step, x + src1_offset - dst_align); - int src2_index = mad24(y, src2_step, x + src2_offset - dst_align); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + x & (int)0xfffffffc); - int src1_index_fix = src1_index < 0 ? 0 : src1_index; - int src2_index_fix = src2_index < 0 ? 0 : src2_index; - uchar4 src1_data = vload4(0, src1 + src1_index_fix); - uchar4 src2_data = vload4(0, src2 + src2_index_fix); - if(src1_index < 0) - { - uchar4 tmp; - tmp.xyzw = (src1_index == -2) ? src1_data.zwxy:src1_data.yzwx; - src1_data.xyzw = (src1_index == -1) ? src1_data.wxyz:tmp.xyzw; - } - if(src2_index < 0) - { - uchar4 tmp; - tmp.xyzw = (src2_index == -2) ? src2_data.zwxy:src2_data.yzwx; - src2_data.xyzw = (src2_index == -1) ? src2_data.wxyz:tmp.xyzw; - } - - - - uchar4 dst_data = *((__global uchar4 *)(dst + dst_index)); - uchar4 tmp_data = convert_uchar4((src1_data > src2_data)); - - dst_data.x = ((dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : dst_data.x; - dst_data.y = ((dst_index + 1 >= dst_start) && (dst_index + 1 < dst_end)) ? tmp_data.y : dst_data.y; - dst_data.z = ((dst_index + 2 >= dst_start) && (dst_index + 2 < dst_end)) ? tmp_data.z : dst_data.z; - dst_data.w = ((dst_index + 3 >= dst_start) && (dst_index + 3 < dst_end)) ? tmp_data.w : dst_data.w; - - *((__global uchar4 *)(dst + dst_index)) = dst_data; - } -} - -__kernel void arithm_compare_gt_D2 (__global ushort *src1, int src1_step, int src1_offset, - __global ushort *src2, int src2_step, int src2_offset, - __global uchar *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) - -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 2; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align ((dst_offset >> 1) & 3) - int src1_index = mad24(y, src1_step, (x << 1) + src1_offset - (dst_align << 1)); - int src2_index = mad24(y, src2_step, (x << 1) + src2_offset - (dst_align << 1)); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + x & (int)0xfffffffc); - int src1_index_fix = src1_index < 0 ? 0 : src1_index; - int src2_index_fix = src2_index < 0 ? 0 : src2_index; - ushort4 src1_data = vload4(0, (__global ushort *)((__global char *)src1 + src1_index)); - ushort4 src2_data = vload4(0, (__global ushort *)((__global char *)src2 + src2_index)); - if(src1_index < 0) - { - ushort4 tmp; - tmp.xyzw = (src1_index == -2) ? src1_data.zwxy:src1_data.yzwx; - src1_data.xyzw = (src1_index == -1) ? src1_data.wxyz:tmp.xyzw; - } - if(src2_index < 0) - { - ushort4 tmp; - tmp.xyzw = (src2_index == -2) ? src2_data.zwxy:src2_data.yzwx; - src2_data.xyzw = (src2_index == -1) ? src2_data.wxyz:tmp.xyzw; - } - - - - uchar4 dst_data = *((__global uchar4 *)(dst + dst_index)); - uchar4 tmp_data = convert_uchar4((src1_data > src2_data)); - - dst_data.x = ((dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : dst_data.x; - dst_data.y = ((dst_index + 1 >= dst_start) && (dst_index + 1 < dst_end)) ? tmp_data.y : dst_data.y; - dst_data.z = ((dst_index + 2 >= dst_start) && (dst_index + 2 < dst_end)) ? tmp_data.z : dst_data.z; - dst_data.w = ((dst_index + 3 >= dst_start) && (dst_index + 3 < dst_end)) ? tmp_data.w : dst_data.w; - - *((__global uchar4 *)(dst + dst_index)) = dst_data; - } -} - - - -__kernel void arithm_compare_gt_D3 (__global short *src1, int src1_step, int src1_offset, - __global short *src2, int src2_step, int src2_offset, - __global uchar *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) - -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 2; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align ((dst_offset >> 1) & 3) - int src1_index = mad24(y, src1_step, (x << 1) + src1_offset - (dst_align << 1)); - int src2_index = mad24(y, src2_step, (x << 1) + src2_offset - (dst_align << 1)); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + x & (int)0xfffffffc); - int src1_index_fix = src1_index < 0 ? 0 : src1_index; - int src2_index_fix = src2_index < 0 ? 0 : src2_index; - short4 src1_data = vload4(0, (__global short *)((__global char *)src1 + src1_index)); - short4 src2_data = vload4(0, (__global short *)((__global char *)src2 + src2_index)); - if(src1_index < 0) - { - short4 tmp; - tmp.xyzw = (src1_index == -2) ? src1_data.zwxy:src1_data.yzwx; - src1_data.xyzw = (src1_index == -1) ? src1_data.wxyz:tmp.xyzw; - } - if(src2_index < 0) - { - short4 tmp; - tmp.xyzw = (src2_index == -2) ? src2_data.zwxy:src2_data.yzwx; - src2_data.xyzw = (src2_index == -1) ? src2_data.wxyz:tmp.xyzw; - } - - - - uchar4 dst_data = *((__global uchar4 *)(dst + dst_index)); - uchar4 tmp_data = convert_uchar4((src1_data > src2_data)); - - dst_data.x = ((dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : dst_data.x; - dst_data.y = ((dst_index + 1 >= dst_start) && (dst_index + 1 < dst_end)) ? tmp_data.y : dst_data.y; - dst_data.z = ((dst_index + 2 >= dst_start) && (dst_index + 2 < dst_end)) ? tmp_data.z : dst_data.z; - dst_data.w = ((dst_index + 3 >= dst_start) && (dst_index + 3 < dst_end)) ? tmp_data.w : dst_data.w; - - *((__global uchar4 *)(dst + dst_index)) = dst_data; - } -} - -__kernel void arithm_compare_gt_D4 (__global int *src1, int src1_step, int src1_offset, - __global int *src2, int src2_step, int src2_offset, - __global uchar *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 2; -#ifdef dst_align -#undef dst_align -#endif -#define dst_align ((dst_offset >> 2) & 3) - int src1_index = mad24(y, src1_step, (x << 2) + src1_offset - (dst_align << 2)); - int src2_index = mad24(y, src2_step, (x << 2) + src2_offset - (dst_align << 2)); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + x & (int)0xfffffffc); - int src1_index_fix = src1_index < 0 ? 0 : src1_index; - int src2_index_fix = src2_index < 0 ? 0 : src2_index; - - int4 src1_data = vload4(0, (__global int *)((__global char *)src1 + src1_index)); - int4 src2_data = vload4(0, (__global int *)((__global char *)src2 + src2_index)); - if(src1_index < 0) - { - int4 tmp; - tmp.xyzw = (src1_index == -2) ? src1_data.zwxy:src1_data.yzwx; - src1_data.xyzw = (src1_index == -1) ? src1_data.wxyz:tmp.xyzw; - } - if(src2_index < 0) - { - int4 tmp; - tmp.xyzw = (src2_index == -2) ? src2_data.zwxy:src2_data.yzwx; - src2_data.xyzw = (src2_index == -1) ? src2_data.wxyz:tmp.xyzw; - } - - - uchar4 dst_data = *((__global uchar4 *)(dst + dst_index)); - uchar4 tmp_data = convert_uchar4((src1_data > src2_data)); - - dst_data.x = ((dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : dst_data.x; - dst_data.y = ((dst_index + 1 >= dst_start) && (dst_index + 1 < dst_end)) ? tmp_data.y : dst_data.y; - dst_data.z = ((dst_index + 2 >= dst_start) && (dst_index + 2 < dst_end)) ? tmp_data.z : dst_data.z; - dst_data.w = ((dst_index + 3 >= dst_start) && (dst_index + 3 < dst_end)) ? tmp_data.w : dst_data.w; - - *((__global uchar4 *)(dst + dst_index)) = dst_data; - } -} - -__kernel void arithm_compare_gt_D5 (__global float *src1, int src1_step, int src1_offset, - __global float *src2, int src2_step, int src2_offset, - __global uchar *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 2; -#ifdef dst_align -#undef dst_align -#endif -#define dst_align ((dst_offset >> 2) & 3) - int src1_index = mad24(y, src1_step, (x << 2) + src1_offset - (dst_align << 2)); - int src2_index = mad24(y, src2_step, (x << 2) + src2_offset - (dst_align << 2)); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + x & (int)0xfffffffc); - int src1_index_fix = src1_index < 0 ? 0 : src1_index; - int src2_index_fix = src2_index < 0 ? 0 : src2_index; - float4 src1_data = vload4(0, (__global float *)((__global char *)src1 + src1_index_fix)); - float4 src2_data = vload4(0, (__global float *)((__global char *)src2 + src2_index_fix)); - if(src1_index < 0) - { - float4 tmp; - tmp.xyzw = (src1_index == -2) ? src1_data.zwxy:src1_data.yzwx; - src1_data.xyzw = (src1_index == -1) ? src1_data.wxyz:tmp.xyzw; - } - if(src2_index < 0) - { - float4 tmp; - tmp.xyzw = (src2_index == -2) ? src2_data.zwxy:src2_data.yzwx; - src2_data.xyzw = (src2_index == -1) ? src2_data.wxyz:tmp.xyzw; - } - - - uchar4 dst_data = *((__global uchar4 *)(dst + dst_index)); - uchar4 tmp_data = convert_uchar4((src1_data > src2_data)); - - dst_data.x = ((dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : dst_data.x; - dst_data.y = ((dst_index + 1 >= dst_start) && (dst_index + 1 < dst_end)) ? tmp_data.y : dst_data.y; - dst_data.z = ((dst_index + 2 >= dst_start) && (dst_index + 2 < dst_end)) ? tmp_data.z : dst_data.z; - dst_data.w = ((dst_index + 3 >= dst_start) && (dst_index + 3 < dst_end)) ? tmp_data.w : dst_data.w; - - *((__global uchar4 *)(dst + dst_index)) = dst_data; - } -} - -#if defined (DOUBLE_SUPPORT) -__kernel void arithm_compare_gt_D6 (__global double *src1, int src1_step, int src1_offset, - __global double *src2, int src2_step, int src2_offset, - __global uchar *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 2; -#ifdef dst_align -#undef dst_align -#endif -#define dst_align ((dst_offset >> 3) & 3) - int src1_index = mad24(y, src1_step, (x << 3) + src1_offset - (dst_align << 3)); - int src2_index = mad24(y, src2_step, (x << 3) + src2_offset - (dst_align << 3)); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + x & (int)0xfffffffc); - int src1_index_fix = src1_index < 0 ? 0 : src1_index; - int src2_index_fix = src2_index < 0 ? 0 : src2_index; - double4 src1_data = vload4(0, (__global double *)((__global char *)src1 + src1_index_fix)); - double4 src2_data = vload4(0, (__global double *)((__global char *)src2 + src2_index_fix)); - if(src1_index < 0) - { - double4 tmp; - tmp.xyzw = (src1_index == -2) ? src1_data.zwxy:src1_data.yzwx; - src1_data.xyzw = (src1_index == -1) ? src1_data.wxyz:tmp.xyzw; - } - if(src2_index < 0) - { - double4 tmp; - tmp.xyzw = (src2_index == -2) ? src2_data.zwxy:src2_data.yzwx; - src2_data.xyzw = (src2_index == -1) ? src2_data.wxyz:tmp.xyzw; - } - - - uchar4 dst_data = *((__global uchar4 *)(dst + dst_index)); - uchar4 tmp_data = convert_uchar4((src1_data > src2_data)); - - dst_data.x = ((dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : dst_data.x; - dst_data.y = ((dst_index + 1 >= dst_start) && (dst_index + 1 < dst_end)) ? tmp_data.y : dst_data.y; - dst_data.z = ((dst_index + 2 >= dst_start) && (dst_index + 2 < dst_end)) ? tmp_data.z : dst_data.z; - dst_data.w = ((dst_index + 3 >= dst_start) && (dst_index + 3 < dst_end)) ? tmp_data.w : dst_data.w; - - *((__global uchar4 *)(dst + dst_index)) = dst_data; - } -} -#endif - -/***********************************Compare GE**************************/ -__kernel void arithm_compare_ge_D0 (__global uchar *src1, int src1_step, int src1_offset, - __global uchar *src2, int src2_step, int src2_offset, - __global uchar *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 2; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align (dst_offset & 3) - int src1_index = mad24(y, src1_step, x + src1_offset - dst_align); - int src2_index = mad24(y, src2_step, x + src2_offset - dst_align); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + x & (int)0xfffffffc); - - int src1_index_fix = src1_index < 0 ? 0 : src1_index; - int src2_index_fix = src2_index < 0 ? 0 : src2_index; - uchar4 src1_data = vload4(0, src1 + src1_index_fix); - uchar4 src2_data = vload4(0, src2 + src2_index_fix); - if(src1_index < 0) - { - uchar4 tmp; - tmp.xyzw = (src1_index == -2) ? src1_data.zwxy:src1_data.yzwx; - src1_data.xyzw = (src1_index == -1) ? src1_data.wxyz:tmp.xyzw; - } - if(src2_index < 0) - { - uchar4 tmp; - tmp.xyzw = (src2_index == -2) ? src2_data.zwxy:src2_data.yzwx; - src2_data.xyzw = (src2_index == -1) ? src2_data.wxyz:tmp.xyzw; - } - - - - uchar4 dst_data = *((__global uchar4 *)(dst + dst_index)); - uchar4 tmp_data = convert_uchar4((src1_data >= src2_data)); - - dst_data.x = ((dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : dst_data.x; - dst_data.y = ((dst_index + 1 >= dst_start) && (dst_index + 1 < dst_end)) ? tmp_data.y : dst_data.y; - dst_data.z = ((dst_index + 2 >= dst_start) && (dst_index + 2 < dst_end)) ? tmp_data.z : dst_data.z; - dst_data.w = ((dst_index + 3 >= dst_start) && (dst_index + 3 < dst_end)) ? tmp_data.w : dst_data.w; - - *((__global uchar4 *)(dst + dst_index)) = dst_data; - } -} - - - -__kernel void arithm_compare_ge_D2 (__global ushort *src1, int src1_step, int src1_offset, - __global ushort *src2, int src2_step, int src2_offset, - __global uchar *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) - -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 2; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align ((dst_offset >> 1) & 3) - int src1_index = mad24(y, src1_step, (x << 1) + src1_offset - (dst_align << 1)); - int src2_index = mad24(y, src2_step, (x << 1) + src2_offset - (dst_align << 1)); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + x & (int)0xfffffffc); - - int src1_index_fix = src1_index < 0 ? 0 : src1_index; - int src2_index_fix = src2_index < 0 ? 0 : src2_index; - ushort4 src1_data = vload4(0, (__global ushort *)((__global char *)src1 + src1_index)); - ushort4 src2_data = vload4(0, (__global ushort *)((__global char *)src2 + src2_index)); - if(src1_index < 0) - { - ushort4 tmp; - tmp.xyzw = (src1_index == -2) ? src1_data.zwxy:src1_data.yzwx; - src1_data.xyzw = (src1_index == -1) ? src1_data.wxyz:tmp.xyzw; - } - if(src2_index < 0) - { - ushort4 tmp; - tmp.xyzw = (src2_index == -2) ? src2_data.zwxy:src2_data.yzwx; - src2_data.xyzw = (src2_index == -1) ? src2_data.wxyz:tmp.xyzw; - } - - - - - uchar4 dst_data = *((__global uchar4 *)(dst + dst_index)); - uchar4 tmp_data = convert_uchar4((src1_data >= src2_data)); - - dst_data.x = ((dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : dst_data.x; - dst_data.y = ((dst_index + 1 >= dst_start) && (dst_index + 1 < dst_end)) ? tmp_data.y : dst_data.y; - dst_data.z = ((dst_index + 2 >= dst_start) && (dst_index + 2 < dst_end)) ? tmp_data.z : dst_data.z; - dst_data.w = ((dst_index + 3 >= dst_start) && (dst_index + 3 < dst_end)) ? tmp_data.w : dst_data.w; - - *((__global uchar4 *)(dst + dst_index)) = dst_data; - } -} - - - -__kernel void arithm_compare_ge_D3 (__global short *src1, int src1_step, int src1_offset, - __global short *src2, int src2_step, int src2_offset, - __global uchar *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) - -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 2; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align ((dst_offset >> 1)& 3) - int src1_index = mad24(y, src1_step, (x << 1) + src1_offset - (dst_align << 1)); - int src2_index = mad24(y, src2_step, (x << 1) + src2_offset - (dst_align << 1)); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + x & (int)0xfffffffc); - - int src1_index_fix = src1_index < 0 ? 0 : src1_index; - int src2_index_fix = src2_index < 0 ? 0 : src2_index; - short4 src1_data = vload4(0, (__global short *)((__global char *)src1 + src1_index)); - short4 src2_data = vload4(0, (__global short *)((__global char *)src2 + src2_index)); - if(src1_index < 0) - { - short4 tmp; - tmp.xyzw = (src1_index == -2) ? src1_data.zwxy:src1_data.yzwx; - src1_data.xyzw = (src1_index == -1) ? src1_data.wxyz:tmp.xyzw; - } - if(src2_index < 0) - { - short4 tmp; - tmp.xyzw = (src2_index == -2) ? src2_data.zwxy:src2_data.yzwx; - src2_data.xyzw = (src2_index == -1) ? src2_data.wxyz:tmp.xyzw; - } - - - - uchar4 dst_data = *((__global uchar4 *)(dst + dst_index)); - uchar4 tmp_data = convert_uchar4((src1_data >= src2_data)); - - dst_data.x = ((dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : dst_data.x; - dst_data.y = ((dst_index + 1 >= dst_start) && (dst_index + 1 < dst_end)) ? tmp_data.y : dst_data.y; - dst_data.z = ((dst_index + 2 >= dst_start) && (dst_index + 2 < dst_end)) ? tmp_data.z : dst_data.z; - dst_data.w = ((dst_index + 3 >= dst_start) && (dst_index + 3 < dst_end)) ? tmp_data.w : dst_data.w; - - *((__global uchar4 *)(dst + dst_index)) = dst_data; - } -} - -__kernel void arithm_compare_ge_D4 (__global int *src1, int src1_step, int src1_offset, - __global int *src2, int src2_step, int src2_offset, - __global uchar *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 2; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align ((dst_offset >> 2)& 3) - int src1_index = mad24(y, src1_step, (x << 2) + src1_offset - (dst_align << 2)); - int src2_index = mad24(y, src2_step, (x << 2) + src2_offset - (dst_align << 2)); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + x & (int)0xfffffffc); - - int src1_index_fix = src1_index < 0 ? 0 : src1_index; - int src2_index_fix = src2_index < 0 ? 0 : src2_index; - - int4 src1_data = vload4(0, (__global int *)((__global char *)src1 + src1_index)); - int4 src2_data = vload4(0, (__global int *)((__global char *)src2 + src2_index)); - if(src1_index < 0) - { - int4 tmp; - tmp.xyzw = (src1_index == -2) ? src1_data.zwxy:src1_data.yzwx; - src1_data.xyzw = (src1_index == -1) ? src1_data.wxyz:tmp.xyzw; - } - if(src2_index < 0) - { - int4 tmp; - tmp.xyzw = (src2_index == -2) ? src2_data.zwxy:src2_data.yzwx; - src2_data.xyzw = (src2_index == -1) ? src2_data.wxyz:tmp.xyzw; - } - uchar4 dst_data = *((__global uchar4 *)(dst + dst_index)); - uchar4 tmp_data = convert_uchar4((src1_data >= src2_data)); - - dst_data.x = ((dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : dst_data.x; - dst_data.y = ((dst_index + 1 >= dst_start) && (dst_index + 1 < dst_end)) ? tmp_data.y : dst_data.y; - dst_data.z = ((dst_index + 2 >= dst_start) && (dst_index + 2 < dst_end)) ? tmp_data.z : dst_data.z; - dst_data.w = ((dst_index + 3 >= dst_start) && (dst_index + 3 < dst_end)) ? tmp_data.w : dst_data.w; - - *((__global uchar4 *)(dst + dst_index)) = dst_data; - } -} - -__kernel void arithm_compare_ge_D5 (__global float *src1, int src1_step, int src1_offset, - __global float *src2, int src2_step, int src2_offset, - __global uchar *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 2; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align ((dst_offset >> 2)& 3) - int src1_index = mad24(y, src1_step, (x << 2) + src1_offset - (dst_align << 2)); - int src2_index = mad24(y, src2_step, (x << 2) + src2_offset - (dst_align << 2)); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + x & (int)0xfffffffc); - - int src1_index_fix = src1_index < 0 ? 0 : src1_index; - int src2_index_fix = src2_index < 0 ? 0 : src2_index; - float4 src1_data = vload4(0, (__global float *)((__global char *)src1 + src1_index_fix)); - float4 src2_data = vload4(0, (__global float *)((__global char *)src2 + src2_index_fix)); - if(src1_index < 0) - { - - float4 tmp; - tmp.xyzw = (src1_index == -2) ? src1_data.zwxy:src1_data.yzwx; - src1_data.xyzw = (src1_index == -1) ? src1_data.wxyz:tmp.xyzw; - } - if(src2_index < 0) - { - float4 tmp; - tmp.xyzw = (src2_index == -2) ? src2_data.zwxy:src2_data.yzwx; - src2_data.xyzw = (src2_index == -1) ? src2_data.wxyz:tmp.xyzw; - } - - uchar4 dst_data = *((__global uchar4 *)(dst + dst_index)); - uchar4 tmp_data = convert_uchar4((src1_data >= src2_data)); - - dst_data.x = ((dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : dst_data.x; - dst_data.y = ((dst_index + 1 >= dst_start) && (dst_index + 1 < dst_end)) ? tmp_data.y : dst_data.y; - dst_data.z = ((dst_index + 2 >= dst_start) && (dst_index + 2 < dst_end)) ? tmp_data.z : dst_data.z; - dst_data.w = ((dst_index + 3 >= dst_start) && (dst_index + 3 < dst_end)) ? tmp_data.w : dst_data.w; - - *((__global uchar4 *)(dst + dst_index)) = dst_data; - } -} - -#if defined (DOUBLE_SUPPORT) -__kernel void arithm_compare_ge_D6 (__global double *src1, int src1_step, int src1_offset, - __global double *src2, int src2_step, int src2_offset, - __global uchar *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 2; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align ((dst_offset >> 3)& 3) - int src1_index = mad24(y, src1_step, (x << 3) + src1_offset - (dst_align << 3)); - int src2_index = mad24(y, src2_step, (x << 3) + src2_offset - (dst_align << 3)); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + x & (int)0xfffffffc); - int src1_index_fix = src1_index < 0 ? 0 : src1_index; - int src2_index_fix = src2_index < 0 ? 0 : src2_index; - double4 src1_data = vload4(0, (__global double *)((__global char *)src1 + src1_index_fix)); - double4 src2_data = vload4(0, (__global double *)((__global char *)src2 + src2_index_fix)); - if(src1_index < 0) - { - double4 tmp; - tmp.xyzw = (src1_index == -2) ? src1_data.zwxy:src1_data.yzwx; - src1_data.xyzw = (src1_index == -1) ? src1_data.wxyz:tmp.xyzw; - } - if(src2_index < 0) - { - double4 tmp; - tmp.xyzw = (src2_index == -2) ? src2_data.zwxy:src2_data.yzwx; - src2_data.xyzw = (src2_index == -1) ? src2_data.wxyz:tmp.xyzw; - } - uchar4 dst_data = *((__global uchar4 *)(dst + dst_index)); - uchar4 tmp_data = convert_uchar4((src1_data >= src2_data)); - - dst_data.x = ((dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : dst_data.x; - dst_data.y = ((dst_index + 1 >= dst_start) && (dst_index + 1 < dst_end)) ? tmp_data.y : dst_data.y; - dst_data.z = ((dst_index + 2 >= dst_start) && (dst_index + 2 < dst_end)) ? tmp_data.z : dst_data.z; - dst_data.w = ((dst_index + 3 >= dst_start) && (dst_index + 3 < dst_end)) ? tmp_data.w : dst_data.w; - - *((__global uchar4 *)(dst + dst_index)) = dst_data; - } -} -#endif diff --git a/modules/ocl/src/opencl/arithm_compare_ne.cl b/modules/ocl/src/opencl/arithm_compare_ne.cl deleted file mode 100644 index fb5859d3b2..0000000000 --- a/modules/ocl/src/opencl/arithm_compare_ne.cl +++ /dev/null @@ -1,1013 +0,0 @@ -/*M/////////////////////////////////////////////////////////////////////////////////////// -// -// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. -// -// By downloading, copying, installing or using the software you agree to this license. -// If you do not agree to this license, do not download, install, -// copy or use the software. -// -// -// License Agreement -// For Open Source Computer Vision Library -// -// Copyright (C) 2010-2012, Institute Of Software Chinese Academy Of Science, all rights reserved. -// Copyright (C) 2010-2012, Advanced Micro Devices, Inc., all rights reserved. -// Third party copyrights are property of their respective owners. -// -// @Authors -// Jiang Liyuan, jlyuan001.good@163.com -// -// Redistribution and use in source and binary forms, with or without modification, -// are permitted provided that the following conditions are met: -// -// * Redistribution's of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// * Redistribution's in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other oclMaterials provided with the distribution. -// -// * The name of the copyright holders may not be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// This software is provided by the copyright holders and contributors as is and -// any express or implied warranties, including, but not limited to, the implied -// warranties of merchantability and fitness for a particular purpose are disclaimed. -// In no event shall the Intel Corporation or contributors be liable for any direct, -// indirect, incidental, special, exemplary, or consequential damages -// (including, but not limited to, procurement of substitute goods or services; -// loss of use, data, or profits; or business interruption) however caused -// and on any theory of liability, whether in contract, strict liability, -// or tort (including negligence or otherwise) arising in any way out of -// the use of this software, even if advised of the possibility of such damage. -// -//M*/ -#if defined (DOUBLE_SUPPORT) -#ifdef cl_khr_fp64 -#pragma OPENCL EXTENSION cl_khr_fp64:enable -#elif defined (cl_amd_fp64) -#pragma OPENCL EXTENSION cl_amd_fp64:enable -#endif -#endif -/***********************************Compare NE*******************************/ -__kernel void arithm_compare_ne_D0 (__global uchar *src1, int src1_step, int src1_offset, - __global uchar *src2, int src2_step, int src2_offset, - __global uchar *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 2; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align (dst_offset & 3) - int src1_index = mad24(y, src1_step, x + src1_offset - dst_align); - int src2_index = mad24(y, src2_step, x + src2_offset - dst_align); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + x & (int)0xfffffffc); - int src1_index_fix = src1_index < 0 ? 0 : src1_index; - int src2_index_fix = src2_index < 0 ? 0 : src2_index; - uchar4 src1_data = vload4(0, src1 + src1_index_fix); - uchar4 src2_data = vload4(0, src2 + src2_index_fix); - if(src1_index < 0) - { - uchar4 tmp; - tmp.xyzw = (src1_index == -2) ? src1_data.zwxy:src1_data.yzwx; - src1_data.xyzw = (src1_index == -1) ? src1_data.wxyz:tmp.xyzw; - } - if(src2_index < 0) - { - uchar4 tmp; - tmp.xyzw = (src2_index == -2) ? src2_data.zwxy:src2_data.yzwx; - src2_data.xyzw = (src2_index == -1) ? src2_data.wxyz:tmp.xyzw; - } - - - uchar4 dst_data = *((__global uchar4 *)(dst + dst_index)); - uchar4 tmp_data = convert_uchar4((src1_data != src2_data)); - - dst_data.x = ((dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : dst_data.x; - dst_data.y = ((dst_index + 1 >= dst_start) && (dst_index + 1 < dst_end)) ? tmp_data.y : dst_data.y; - dst_data.z = ((dst_index + 2 >= dst_start) && (dst_index + 2 < dst_end)) ? tmp_data.z : dst_data.z; - dst_data.w = ((dst_index + 3 >= dst_start) && (dst_index + 3 < dst_end)) ? tmp_data.w : dst_data.w; - - *((__global uchar4 *)(dst + dst_index)) = dst_data; - } -} - - - -__kernel void arithm_compare_ne_D2 (__global ushort *src1, int src1_step, int src1_offset, - __global ushort *src2, int src2_step, int src2_offset, - __global uchar *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) - -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 2; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align ((dst_offset >> 1)& 3) - int src1_index = mad24(y, src1_step, (x << 1) + src1_offset - (dst_align << 1)); - int src2_index = mad24(y, src2_step, (x << 1) + src2_offset - (dst_align << 1)); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + x & (int)0xfffffffc); - - int src1_index_fix = src1_index < 0 ? 0 : src1_index; - int src2_index_fix = src2_index < 0 ? 0 : src2_index; - ushort4 src1_data = vload4(0, (__global ushort *)((__global char *)src1 + src1_index)); - ushort4 src2_data = vload4(0, (__global ushort *)((__global char *)src2 + src2_index)); - if(src1_index < 0) - { - ushort4 tmp; - tmp.xyzw = (src1_index == -2) ? src1_data.zwxy:src1_data.yzwx; - src1_data.xyzw = (src1_index == -1) ? src1_data.wxyz:tmp.xyzw; - } - if(src2_index < 0) - { - ushort4 tmp; - tmp.xyzw = (src2_index == -2) ? src2_data.zwxy:src2_data.yzwx; - src2_data.xyzw = (src2_index == -1) ? src2_data.wxyz:tmp.xyzw; - } - - uchar4 dst_data = *((__global uchar4 *)(dst + dst_index)); - uchar4 tmp_data = convert_uchar4((src1_data != src2_data)); - - dst_data.x = ((dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : dst_data.x; - dst_data.y = ((dst_index + 1 >= dst_start) && (dst_index + 1 < dst_end)) ? tmp_data.y : dst_data.y; - dst_data.z = ((dst_index + 2 >= dst_start) && (dst_index + 2 < dst_end)) ? tmp_data.z : dst_data.z; - dst_data.w = ((dst_index + 3 >= dst_start) && (dst_index + 3 < dst_end)) ? tmp_data.w : dst_data.w; - - *((__global uchar4 *)(dst + dst_index)) = dst_data; - } -} - - - -__kernel void arithm_compare_ne_D3 (__global short *src1, int src1_step, int src1_offset, - __global short *src2, int src2_step, int src2_offset, - __global uchar *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) - -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 2; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align ((dst_offset >> 1)& 3) - int src1_index = mad24(y, src1_step, (x << 1) + src1_offset - (dst_align << 1)); - int src2_index = mad24(y, src2_step, (x << 1) + src2_offset - (dst_align << 1)); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + x & (int)0xfffffffc); - int src1_index_fix = src1_index < 0 ? 0 : src1_index; - int src2_index_fix = src2_index < 0 ? 0 : src2_index; - short4 src1_data = vload4(0, (__global short *)((__global char *)src1 + src1_index)); - short4 src2_data = vload4(0, (__global short *)((__global char *)src2 + src2_index)); - if(src1_index < 0) - { - short4 tmp; - tmp.xyzw = (src1_index == -2) ? src1_data.zwxy:src1_data.yzwx; - src1_data.xyzw = (src1_index == -1) ? src1_data.wxyz:tmp.xyzw; - } - if(src2_index < 0) - { - short4 tmp; - tmp.xyzw = (src2_index == -2) ? src2_data.zwxy:src2_data.yzwx; - src2_data.xyzw = (src2_index == -1) ? src2_data.wxyz:tmp.xyzw; - } - - - uchar4 dst_data = *((__global uchar4 *)(dst + dst_index)); - uchar4 tmp_data = convert_uchar4((src1_data != src2_data)); - - dst_data.x = ((dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : dst_data.x; - dst_data.y = ((dst_index + 1 >= dst_start) && (dst_index + 1 < dst_end)) ? tmp_data.y : dst_data.y; - dst_data.z = ((dst_index + 2 >= dst_start) && (dst_index + 2 < dst_end)) ? tmp_data.z : dst_data.z; - dst_data.w = ((dst_index + 3 >= dst_start) && (dst_index + 3 < dst_end)) ? tmp_data.w : dst_data.w; - - *((__global uchar4 *)(dst + dst_index)) = dst_data; - } -} - -__kernel void arithm_compare_ne_D4 (__global int *src1, int src1_step, int src1_offset, - __global int *src2, int src2_step, int src2_offset, - __global uchar *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 2; -#ifdef dst_align -#undef dst_align -#endif -#define dst_align ((dst_offset >> 2)& 3) - int src1_index = mad24(y, src1_step, (x << 2) + src1_offset - (dst_align << 2)); - int src2_index = mad24(y, src2_step, (x << 2) + src2_offset - (dst_align << 2)); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + x & (int)0xfffffffc); - - int src1_index_fix = src1_index < 0 ? 0 : src1_index; - int src2_index_fix = src2_index < 0 ? 0 : src2_index; - - int4 src1_data = vload4(0, (__global int *)((__global char *)src1 + src1_index)); - int4 src2_data = vload4(0, (__global int *)((__global char *)src2 + src2_index)); - if(src1_index < 0) - { - int4 tmp; - tmp.xyzw = (src1_index == -2) ? src1_data.zwxy:src1_data.yzwx; - src1_data.xyzw = (src1_index == -1) ? src1_data.wxyz:tmp.xyzw; - } - if(src2_index < 0) - { - int4 tmp; - tmp.xyzw = (src2_index == -2) ? src2_data.zwxy:src2_data.yzwx; - src2_data.xyzw = (src2_index == -1) ? src2_data.wxyz:tmp.xyzw; - } - - uchar4 dst_data = *((__global uchar4 *)(dst + dst_index)); - uchar4 tmp_data = convert_uchar4((src1_data != src2_data)); - - dst_data.x = ((dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : dst_data.x; - dst_data.y = ((dst_index + 1 >= dst_start) && (dst_index + 1 < dst_end)) ? tmp_data.y : dst_data.y; - dst_data.z = ((dst_index + 2 >= dst_start) && (dst_index + 2 < dst_end)) ? tmp_data.z : dst_data.z; - dst_data.w = ((dst_index + 3 >= dst_start) && (dst_index + 3 < dst_end)) ? tmp_data.w : dst_data.w; - - *((__global uchar4 *)(dst + dst_index)) = dst_data; - } -} - -__kernel void arithm_compare_ne_D5 (__global float *src1, int src1_step, int src1_offset, - __global float *src2, int src2_step, int src2_offset, - __global uchar *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 2; -#ifdef dst_align -#undef dst_align -#endif -#define dst_align ((dst_offset >> 2) & 3) - int src1_index = mad24(y, src1_step, (x << 2) + src1_offset - (dst_align << 2)); - int src2_index = mad24(y, src2_step, (x << 2) + src2_offset - (dst_align << 2)); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + x & (int)0xfffffffc); - int src1_index_fix = src1_index < 0 ? 0 : src1_index; - int src2_index_fix = src2_index < 0 ? 0 : src2_index; - float4 src1_data = vload4(0, (__global float *)((__global char *)src1 + src1_index_fix)); - float4 src2_data = vload4(0, (__global float *)((__global char *)src2 + src2_index_fix)); - if(src1_index < 0) - { - float4 tmp; - tmp.xyzw = (src1_index == -2) ? src1_data.zwxy:src1_data.yzwx; - src1_data.xyzw = (src1_index == -1) ? src1_data.wxyz:tmp.xyzw; - } - if(src2_index < 0) - { - float4 tmp; - tmp.xyzw = (src2_index == -2) ? src2_data.zwxy:src2_data.yzwx; - src2_data.xyzw = (src2_index == -1) ? src2_data.wxyz:tmp.xyzw; - } - - uchar4 dst_data = *((__global uchar4 *)(dst + dst_index)); - uchar4 tmp_data = convert_uchar4((src1_data != src2_data)); - - dst_data.x = ((dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : dst_data.x; - dst_data.y = ((dst_index + 1 >= dst_start) && (dst_index + 1 < dst_end)) ? tmp_data.y : dst_data.y; - dst_data.z = ((dst_index + 2 >= dst_start) && (dst_index + 2 < dst_end)) ? tmp_data.z : dst_data.z; - dst_data.w = ((dst_index + 3 >= dst_start) && (dst_index + 3 < dst_end)) ? tmp_data.w : dst_data.w; - - *((__global uchar4 *)(dst + dst_index)) = dst_data; - } -} - -#if defined (DOUBLE_SUPPORT) -__kernel void arithm_compare_ne_D6 (__global double *src1, int src1_step, int src1_offset, - __global double *src2, int src2_step, int src2_offset, - __global uchar *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 2; -#ifdef dst_align -#undef dst_align -#endif -#define dst_align ((dst_offset >> 3) & 3) - int src1_index = mad24(y, src1_step, (x << 3) + src1_offset - (dst_align << 3)); - int src2_index = mad24(y, src2_step, (x << 3) + src2_offset - (dst_align << 3)); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + x & (int)0xfffffffc); - int src1_index_fix = src1_index < 0 ? 0 : src1_index; - int src2_index_fix = src2_index < 0 ? 0 : src2_index; - double4 src1_data = vload4(0, (__global double *)((__global char *)src1 + src1_index_fix)); - double4 src2_data = vload4(0, (__global double *)((__global char *)src2 + src2_index_fix)); - if(src1_index < 0) - { - double4 tmp; - tmp.xyzw = (src1_index == -2) ? src1_data.zwxy:src1_data.yzwx; - src1_data.xyzw = (src1_index == -1) ? src1_data.wxyz:tmp.xyzw; - } - if(src2_index < 0) - { - double4 tmp; - tmp.xyzw = (src2_index == -2) ? src2_data.zwxy:src2_data.yzwx; - src2_data.xyzw = (src2_index == -1) ? src2_data.wxyz:tmp.xyzw; - } - - - uchar4 dst_data = *((__global uchar4 *)(dst + dst_index)); - uchar4 tmp_data = convert_uchar4((src1_data != src2_data)); - - dst_data.x = ((dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : dst_data.x; - dst_data.y = ((dst_index + 1 >= dst_start) && (dst_index + 1 < dst_end)) ? tmp_data.y : dst_data.y; - dst_data.z = ((dst_index + 2 >= dst_start) && (dst_index + 2 < dst_end)) ? tmp_data.z : dst_data.z; - dst_data.w = ((dst_index + 3 >= dst_start) && (dst_index + 3 < dst_end)) ? tmp_data.w : dst_data.w; - - *((__global uchar4 *)(dst + dst_index)) = dst_data; - } -} -#endif - - -/***********************************Compare LT*******************************/ -__kernel void arithm_compare_lt_D0 (__global uchar *src1, int src1_step, int src1_offset, - __global uchar *src2, int src2_step, int src2_offset, - __global uchar *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 2; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align (dst_offset & 3) - int src1_index = mad24(y, src1_step, x + src1_offset - dst_align); - int src2_index = mad24(y, src2_step, x + src2_offset - dst_align); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + x & (int)0xfffffffc); - int src1_index_fix = src1_index < 0 ? 0 : src1_index; - int src2_index_fix = src2_index < 0 ? 0 : src2_index; - uchar4 src1_data = vload4(0, src1 + src1_index_fix); - uchar4 src2_data = vload4(0, src2 + src2_index_fix); - if(src1_index < 0) - { - uchar4 tmp; - tmp.xyzw = (src1_index == -2) ? src1_data.zwxy:src1_data.yzwx; - src1_data.xyzw = (src1_index == -1) ? src1_data.wxyz:tmp.xyzw; - } - if(src2_index < 0) - { - uchar4 tmp; - tmp.xyzw = (src2_index == -2) ? src2_data.zwxy:src2_data.yzwx; - src2_data.xyzw = (src2_index == -1) ? src2_data.wxyz:tmp.xyzw; - } - - - uchar4 dst_data = *((__global uchar4 *)(dst + dst_index)); - uchar4 tmp_data = convert_uchar4((src1_data < src2_data)); - - dst_data.x = ((dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : dst_data.x; - dst_data.y = ((dst_index + 1 >= dst_start) && (dst_index + 1 < dst_end)) ? tmp_data.y : dst_data.y; - dst_data.z = ((dst_index + 2 >= dst_start) && (dst_index + 2 < dst_end)) ? tmp_data.z : dst_data.z; - dst_data.w = ((dst_index + 3 >= dst_start) && (dst_index + 3 < dst_end)) ? tmp_data.w : dst_data.w; - - *((__global uchar4 *)(dst + dst_index)) = dst_data; - } -} - - - -__kernel void arithm_compare_lt_D2 (__global ushort *src1, int src1_step, int src1_offset, - __global ushort *src2, int src2_step, int src2_offset, - __global uchar *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) - -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 2; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align ((dst_offset >> 1) & 3) - int src1_index = mad24(y, src1_step, (x << 1) + src1_offset - (dst_align << 1)); - int src2_index = mad24(y, src2_step, (x << 1) + src2_offset - (dst_align << 1)); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + x & (int)0xfffffffc); - - int src1_index_fix = src1_index < 0 ? 0 : src1_index; - int src2_index_fix = src2_index < 0 ? 0 : src2_index; - ushort4 src1_data = vload4(0, (__global ushort *)((__global char *)src1 + src1_index)); - ushort4 src2_data = vload4(0, (__global ushort *)((__global char *)src2 + src2_index)); - if(src1_index < 0) - { - ushort4 tmp; - tmp.xyzw = (src1_index == -2) ? src1_data.zwxy:src1_data.yzwx; - src1_data.xyzw = (src1_index == -1) ? src1_data.wxyz:tmp.xyzw; - } - if(src2_index < 0) - { - ushort4 tmp; - tmp.xyzw = (src2_index == -2) ? src2_data.zwxy:src2_data.yzwx; - src2_data.xyzw = (src2_index == -1) ? src2_data.wxyz:tmp.xyzw; - } - - - uchar4 dst_data = *((__global uchar4 *)(dst + dst_index)); - uchar4 tmp_data = convert_uchar4((src1_data < src2_data)); - - dst_data.x = ((dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : dst_data.x; - dst_data.y = ((dst_index + 1 >= dst_start) && (dst_index + 1 < dst_end)) ? tmp_data.y : dst_data.y; - dst_data.z = ((dst_index + 2 >= dst_start) && (dst_index + 2 < dst_end)) ? tmp_data.z : dst_data.z; - dst_data.w = ((dst_index + 3 >= dst_start) && (dst_index + 3 < dst_end)) ? tmp_data.w : dst_data.w; - - *((__global uchar4 *)(dst + dst_index)) = dst_data; - } -} - - - -__kernel void arithm_compare_lt_D3 (__global short *src1, int src1_step, int src1_offset, - __global short *src2, int src2_step, int src2_offset, - __global uchar *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) - -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 2; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align ((dst_offset >> 1) & 3) - int src1_index = mad24(y, src1_step, (x << 1) + src1_offset - (dst_align << 1)); - int src2_index = mad24(y, src2_step, (x << 1) + src2_offset - (dst_align << 1)); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + x & (int)0xfffffffc); - int src1_index_fix = src1_index < 0 ? 0 : src1_index; - int src2_index_fix = src2_index < 0 ? 0 : src2_index; - short4 src1_data = vload4(0, (__global short *)((__global char *)src1 + src1_index)); - short4 src2_data = vload4(0, (__global short *)((__global char *)src2 + src2_index)); - if(src1_index < 0) - { - short4 tmp; - tmp.xyzw = (src1_index == -2) ? src1_data.zwxy:src1_data.yzwx; - src1_data.xyzw = (src1_index == -1) ? src1_data.wxyz:tmp.xyzw; - } - if(src2_index < 0) - { - short4 tmp; - tmp.xyzw = (src2_index == -2) ? src2_data.zwxy:src2_data.yzwx; - src2_data.xyzw = (src2_index == -1) ? src2_data.wxyz:tmp.xyzw; - } - - - - uchar4 dst_data = *((__global uchar4 *)(dst + dst_index)); - uchar4 tmp_data = convert_uchar4((src1_data < src2_data)); - - dst_data.x = ((dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : dst_data.x; - dst_data.y = ((dst_index + 1 >= dst_start) && (dst_index + 1 < dst_end)) ? tmp_data.y : dst_data.y; - dst_data.z = ((dst_index + 2 >= dst_start) && (dst_index + 2 < dst_end)) ? tmp_data.z : dst_data.z; - dst_data.w = ((dst_index + 3 >= dst_start) && (dst_index + 3 < dst_end)) ? tmp_data.w : dst_data.w; - - *((__global uchar4 *)(dst + dst_index)) = dst_data; - } -} - -__kernel void arithm_compare_lt_D4 (__global int *src1, int src1_step, int src1_offset, - __global int *src2, int src2_step, int src2_offset, - __global uchar *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 2; -#ifdef dst_align -#undef dst_align -#endif -#define dst_align ((dst_offset >> 2) & 3) - int src1_index = mad24(y, src1_step, (x << 2) + src1_offset - (dst_align << 2)); - int src2_index = mad24(y, src2_step, (x << 2) + src2_offset - (dst_align << 2)); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + x & (int)0xfffffffc); - - int src1_index_fix = src1_index < 0 ? 0 : src1_index; - int src2_index_fix = src2_index < 0 ? 0 : src2_index; - - int4 src1_data = vload4(0, (__global int *)((__global char *)src1 + src1_index)); - int4 src2_data = vload4(0, (__global int *)((__global char *)src2 + src2_index)); - if(src1_index < 0) - { - int4 tmp; - tmp.xyzw = (src1_index == -2) ? src1_data.zwxy:src1_data.yzwx; - src1_data.xyzw = (src1_index == -1) ? src1_data.wxyz:tmp.xyzw; - } - if(src2_index < 0) - { - int4 tmp; - tmp.xyzw = (src2_index == -2) ? src2_data.zwxy:src2_data.yzwx; - src2_data.xyzw = (src2_index == -1) ? src2_data.wxyz:tmp.xyzw; - } - - - - - uchar4 dst_data = *((__global uchar4 *)(dst + dst_index)); - uchar4 tmp_data = convert_uchar4((src1_data < src2_data)); - - dst_data.x = ((dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : dst_data.x; - dst_data.y = ((dst_index + 1 >= dst_start) && (dst_index + 1 < dst_end)) ? tmp_data.y : dst_data.y; - dst_data.z = ((dst_index + 2 >= dst_start) && (dst_index + 2 < dst_end)) ? tmp_data.z : dst_data.z; - dst_data.w = ((dst_index + 3 >= dst_start) && (dst_index + 3 < dst_end)) ? tmp_data.w : dst_data.w; - - *((__global uchar4 *)(dst + dst_index)) = dst_data; - } -} - -__kernel void arithm_compare_lt_D5 (__global float *src1, int src1_step, int src1_offset, - __global float *src2, int src2_step, int src2_offset, - __global uchar *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 2; -#ifdef dst_align -#undef dst_align -#endif -#define dst_align ((dst_offset >> 2) & 3) - int src1_index = mad24(y, src1_step, (x << 2) + src1_offset - (dst_align << 2)); - int src2_index = mad24(y, src2_step, (x << 2) + src2_offset - (dst_align << 2)); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + x & (int)0xfffffffc); - int src1_index_fix = src1_index < 0 ? 0 : src1_index; - int src2_index_fix = src2_index < 0 ? 0 : src2_index; - float4 src1_data = vload4(0, (__global float *)((__global char *)src1 + src1_index_fix)); - float4 src2_data = vload4(0, (__global float *)((__global char *)src2 + src2_index_fix)); - if(src1_index < 0) - { - float4 tmp; - tmp.xyzw = (src1_index == -2) ? src1_data.zwxy:src1_data.yzwx; - src1_data.xyzw = (src1_index == -1) ? src1_data.wxyz:tmp.xyzw; - } - if(src2_index < 0) - { - float4 tmp; - tmp.xyzw = (src2_index == -2) ? src2_data.zwxy:src2_data.yzwx; - src2_data.xyzw = (src2_index == -1) ? src2_data.wxyz:tmp.xyzw; - } - - - uchar4 dst_data = *((__global uchar4 *)(dst + dst_index)); - uchar4 tmp_data = convert_uchar4((src1_data < src2_data)); - - dst_data.x = ((dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : dst_data.x; - dst_data.y = ((dst_index + 1 >= dst_start) && (dst_index + 1 < dst_end)) ? tmp_data.y : dst_data.y; - dst_data.z = ((dst_index + 2 >= dst_start) && (dst_index + 2 < dst_end)) ? tmp_data.z : dst_data.z; - dst_data.w = ((dst_index + 3 >= dst_start) && (dst_index + 3 < dst_end)) ? tmp_data.w : dst_data.w; - - *((__global uchar4 *)(dst + dst_index)) = dst_data; - } -} - -#if defined (DOUBLE_SUPPORT) -__kernel void arithm_compare_lt_D6 (__global double *src1, int src1_step, int src1_offset, - __global double *src2, int src2_step, int src2_offset, - __global uchar *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 2; -#ifdef dst_align -#undef dst_align -#endif -#define dst_align ((dst_offset >> 3) & 3) - int src1_index = mad24(y, src1_step, (x << 3) + src1_offset - (dst_align << 3)); - int src2_index = mad24(y, src2_step, (x << 3) + src2_offset - (dst_align << 3)); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + x & (int)0xfffffffc); - int src1_index_fix = src1_index < 0 ? 0 : src1_index; - int src2_index_fix = src2_index < 0 ? 0 : src2_index; - double4 src1_data = vload4(0, (__global double *)((__global char *)src1 + src1_index_fix)); - double4 src2_data = vload4(0, (__global double *)((__global char *)src2 + src2_index_fix)); - if(src1_index < 0) - { - double4 tmp; - tmp.xyzw = (src1_index == -2) ? src1_data.zwxy:src1_data.yzwx; - src1_data.xyzw = (src1_index == -1) ? src1_data.wxyz:tmp.xyzw; - } - if(src2_index < 0) - { - double4 tmp; - tmp.xyzw = (src2_index == -2) ? src2_data.zwxy:src2_data.yzwx; - src2_data.xyzw = (src2_index == -1) ? src2_data.wxyz:tmp.xyzw; - } - - - uchar4 dst_data = *((__global uchar4 *)(dst + dst_index)); - uchar4 tmp_data = convert_uchar4((src1_data < src2_data)); - - dst_data.x = ((dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : dst_data.x; - dst_data.y = ((dst_index + 1 >= dst_start) && (dst_index + 1 < dst_end)) ? tmp_data.y : dst_data.y; - dst_data.z = ((dst_index + 2 >= dst_start) && (dst_index + 2 < dst_end)) ? tmp_data.z : dst_data.z; - dst_data.w = ((dst_index + 3 >= dst_start) && (dst_index + 3 < dst_end)) ? tmp_data.w : dst_data.w; - - *((__global uchar4 *)(dst + dst_index)) = dst_data; - } -} -#endif - -/***********************************Compare LE*******************************/ -__kernel void arithm_compare_le_D0 (__global uchar *src1, int src1_step, int src1_offset, - __global uchar *src2, int src2_step, int src2_offset, - __global uchar *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 2; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align (dst_offset & 3) - int src1_index = mad24(y, src1_step, x + src1_offset - dst_align); - int src2_index = mad24(y, src2_step, x + src2_offset - dst_align); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + x & (int)0xfffffffc); - int src1_index_fix = src1_index < 0 ? 0 : src1_index; - int src2_index_fix = src2_index < 0 ? 0 : src2_index; - uchar4 src1_data = vload4(0, src1 + src1_index_fix); - uchar4 src2_data = vload4(0, src2 + src2_index_fix); - if(src1_index < 0) - { - uchar4 tmp; - tmp.xyzw = (src1_index == -2) ? src1_data.zwxy:src1_data.yzwx; - src1_data.xyzw = (src1_index == -1) ? src1_data.wxyz:tmp.xyzw; - } - if(src2_index < 0) - { - uchar4 tmp; - tmp.xyzw = (src2_index == -2) ? src2_data.zwxy:src2_data.yzwx; - src2_data.xyzw = (src2_index == -1) ? src2_data.wxyz:tmp.xyzw; - } - - - - uchar4 dst_data = *((__global uchar4 *)(dst + dst_index)); - uchar4 tmp_data = convert_uchar4((src1_data <= src2_data)); - - dst_data.x = ((dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : dst_data.x; - dst_data.y = ((dst_index + 1 >= dst_start) && (dst_index + 1 < dst_end)) ? tmp_data.y : dst_data.y; - dst_data.z = ((dst_index + 2 >= dst_start) && (dst_index + 2 < dst_end)) ? tmp_data.z : dst_data.z; - dst_data.w = ((dst_index + 3 >= dst_start) && (dst_index + 3 < dst_end)) ? tmp_data.w : dst_data.w; - - *((__global uchar4 *)(dst + dst_index)) = dst_data; - } -} - - - -__kernel void arithm_compare_le_D2 (__global ushort *src1, int src1_step, int src1_offset, - __global ushort *src2, int src2_step, int src2_offset, - __global uchar *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) - -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 2; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align ((dst_offset >> 1) & 3) - int src1_index = mad24(y, src1_step, (x << 1) + src1_offset - (dst_align << 1)); - int src2_index = mad24(y, src2_step, (x << 1) + src2_offset - (dst_align << 1)); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + x & (int)0xfffffffc); - int src1_index_fix = src1_index < 0 ? 0 : src1_index; - int src2_index_fix = src2_index < 0 ? 0 : src2_index; - ushort4 src1_data = vload4(0, (__global ushort *)((__global char *)src1 + src1_index)); - ushort4 src2_data = vload4(0, (__global ushort *)((__global char *)src2 + src2_index)); - if(src1_index < 0) - { - ushort4 tmp; - tmp.xyzw = (src1_index == -2) ? src1_data.zwxy:src1_data.yzwx; - src1_data.xyzw = (src1_index == -1) ? src1_data.wxyz:tmp.xyzw; - } - if(src2_index < 0) - { - ushort4 tmp; - tmp.xyzw = (src2_index == -2) ? src2_data.zwxy:src2_data.yzwx; - src2_data.xyzw = (src2_index == -1) ? src2_data.wxyz:tmp.xyzw; - } - - - - uchar4 dst_data = *((__global uchar4 *)(dst + dst_index)); - uchar4 tmp_data = convert_uchar4((src1_data <= src2_data)); - - dst_data.x = ((dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : dst_data.x; - dst_data.y = ((dst_index + 1 >= dst_start) && (dst_index + 1 < dst_end)) ? tmp_data.y : dst_data.y; - dst_data.z = ((dst_index + 2 >= dst_start) && (dst_index + 2 < dst_end)) ? tmp_data.z : dst_data.z; - dst_data.w = ((dst_index + 3 >= dst_start) && (dst_index + 3 < dst_end)) ? tmp_data.w : dst_data.w; - - *((__global uchar4 *)(dst + dst_index)) = dst_data; - } -} - - - -__kernel void arithm_compare_le_D3 (__global short *src1, int src1_step, int src1_offset, - __global short *src2, int src2_step, int src2_offset, - __global uchar *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) - -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 2; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align ((dst_offset >> 1) & 3) - int src1_index = mad24(y, src1_step, (x << 1) + src1_offset - (dst_align << 1)); - int src2_index = mad24(y, src2_step, (x << 1) + src2_offset - (dst_align << 1)); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + x & (int)0xfffffffc); - int src1_index_fix = src1_index < 0 ? 0 : src1_index; - int src2_index_fix = src2_index < 0 ? 0 : src2_index; - short4 src1_data = vload4(0, (__global short *)((__global char *)src1 + src1_index)); - short4 src2_data = vload4(0, (__global short *)((__global char *)src2 + src2_index)); - if(src1_index < 0) - { - short4 tmp; - tmp.xyzw = (src1_index == -2) ? src1_data.zwxy:src1_data.yzwx; - src1_data.xyzw = (src1_index == -1) ? src1_data.wxyz:tmp.xyzw; - } - if(src2_index < 0) - { - short4 tmp; - tmp.xyzw = (src2_index == -2) ? src2_data.zwxy:src2_data.yzwx; - src2_data.xyzw = (src2_index == -1) ? src2_data.wxyz:tmp.xyzw; - } - - - - uchar4 dst_data = *((__global uchar4 *)(dst + dst_index)); - uchar4 tmp_data = convert_uchar4((src1_data <= src2_data)); - - dst_data.x = ((dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : dst_data.x; - dst_data.y = ((dst_index + 1 >= dst_start) && (dst_index + 1 < dst_end)) ? tmp_data.y : dst_data.y; - dst_data.z = ((dst_index + 2 >= dst_start) && (dst_index + 2 < dst_end)) ? tmp_data.z : dst_data.z; - dst_data.w = ((dst_index + 3 >= dst_start) && (dst_index + 3 < dst_end)) ? tmp_data.w : dst_data.w; - - *((__global uchar4 *)(dst + dst_index)) = dst_data; - } -} - -__kernel void arithm_compare_le_D4 (__global int *src1, int src1_step, int src1_offset, - __global int *src2, int src2_step, int src2_offset, - __global uchar *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 2; -#ifdef dst_align -#undef dst_align -#endif -#define dst_align ((dst_offset >> 2)& 3) - int src1_index = mad24(y, src1_step, (x << 2) + src1_offset - (dst_align << 2)); - int src2_index = mad24(y, src2_step, (x << 2) + src2_offset - (dst_align << 2)); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + x & (int)0xfffffffc); - int src1_index_fix = src1_index < 0 ? 0 : src1_index; - int src2_index_fix = src2_index < 0 ? 0 : src2_index; - - int4 src1_data = vload4(0, (__global int *)((__global char *)src1 + src1_index)); - int4 src2_data = vload4(0, (__global int *)((__global char *)src2 + src2_index)); - if(src1_index < 0) - { - int4 tmp; - tmp.xyzw = (src1_index == -2) ? src1_data.zwxy:src1_data.yzwx; - src1_data.xyzw = (src1_index == -1) ? src1_data.wxyz:tmp.xyzw; - } - if(src2_index < 0) - { - int4 tmp; - tmp.xyzw = (src2_index == -2) ? src2_data.zwxy:src2_data.yzwx; - src2_data.xyzw = (src2_index == -1) ? src2_data.wxyz:tmp.xyzw; - } - - uchar4 dst_data = *((__global uchar4 *)(dst + dst_index)); - uchar4 tmp_data =convert_uchar4((src1_data <= src2_data)); - - dst_data.x = ((dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : dst_data.x; - dst_data.y = ((dst_index + 1 >= dst_start) && (dst_index + 1 < dst_end)) ? tmp_data.y : dst_data.y; - dst_data.z = ((dst_index + 2 >= dst_start) && (dst_index + 2 < dst_end)) ? tmp_data.z : dst_data.z; - dst_data.w = ((dst_index + 3 >= dst_start) && (dst_index + 3 < dst_end)) ? tmp_data.w : dst_data.w; - - *((__global uchar4 *)(dst + dst_index)) = dst_data; - } -} - -__kernel void arithm_compare_le_D5 (__global float *src1, int src1_step, int src1_offset, - __global float *src2, int src2_step, int src2_offset, - __global uchar *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 2; -#ifdef dst_align -#undef dst_align -#endif -#define dst_align ((dst_offset >> 2)& 3) - int src1_index = mad24(y, src1_step, (x << 2) + src1_offset - (dst_align << 2)); - int src2_index = mad24(y, src2_step, (x << 2) + src2_offset - (dst_align << 2)); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + x & (int)0xfffffffc); - int src1_index_fix = src1_index < 0 ? 0 : src1_index; - int src2_index_fix = src2_index < 0 ? 0 : src2_index; - float4 src1_data = vload4(0, (__global float *)((__global char *)src1 + src1_index_fix)); - float4 src2_data = vload4(0, (__global float *)((__global char *)src2 + src2_index_fix)); - if(src1_index < 0) - { - float4 tmp; - tmp.xyzw = (src1_index == -2) ? src1_data.zwxy:src1_data.yzwx; - src1_data.xyzw = (src1_index == -1) ? src1_data.wxyz:tmp.xyzw; - } - if(src2_index < 0) - { - float4 tmp; - tmp.xyzw = (src2_index == -2) ? src2_data.zwxy:src2_data.yzwx; - src2_data.xyzw = (src2_index == -1) ? src2_data.wxyz:tmp.xyzw; - } - - uchar4 dst_data = *((__global uchar4 *)(dst + dst_index)); - uchar4 tmp_data = convert_uchar4((src1_data <= src2_data)); - - dst_data.x = ((dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : dst_data.x; - dst_data.y = ((dst_index + 1 >= dst_start) && (dst_index + 1 < dst_end)) ? tmp_data.y : dst_data.y; - dst_data.z = ((dst_index + 2 >= dst_start) && (dst_index + 2 < dst_end)) ? tmp_data.z : dst_data.z; - dst_data.w = ((dst_index + 3 >= dst_start) && (dst_index + 3 < dst_end)) ? tmp_data.w : dst_data.w; - - *((__global uchar4 *)(dst + dst_index)) = dst_data; - } -} - -#if defined (DOUBLE_SUPPORT) -__kernel void arithm_compare_le_D6 (__global double *src1, int src1_step, int src1_offset, - __global double *src2, int src2_step, int src2_offset, - __global uchar *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 2; -#ifdef dst_align -#undef dst_align -#endif -#define dst_align ((dst_offset >> 3)& 3) - int src1_index = mad24(y, src1_step, (x << 3) + src1_offset - (dst_align << 3)); - int src2_index = mad24(y, src2_step, (x << 3) + src2_offset - (dst_align << 3)); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + x & (int)0xfffffffc); - int src1_index_fix = src1_index < 0 ? 0 : src1_index; - int src2_index_fix = src2_index < 0 ? 0 : src2_index; - double4 src1_data = vload4(0, (__global double *)((__global char *)src1 + src1_index_fix)); - double4 src2_data = vload4(0, (__global double *)((__global char *)src2 + src2_index_fix)); - if(src1_index < 0) - { - double4 tmp; - tmp.xyzw = (src1_index == -2) ? src1_data.zwxy:src1_data.yzwx; - src1_data.xyzw = (src1_index == -1) ? src1_data.wxyz:tmp.xyzw; - } - if(src2_index < 0) - { - double4 tmp; - tmp.xyzw = (src2_index == -2) ? src2_data.zwxy:src2_data.yzwx; - src2_data.xyzw = (src2_index == -1) ? src2_data.wxyz:tmp.xyzw; - } - - - uchar4 dst_data = *((__global uchar4 *)(dst + dst_index)); - uchar4 tmp_data = convert_uchar4((src1_data <= src2_data)); - - dst_data.x = ((dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : dst_data.x; - dst_data.y = ((dst_index + 1 >= dst_start) && (dst_index + 1 < dst_end)) ? tmp_data.y : dst_data.y; - dst_data.z = ((dst_index + 2 >= dst_start) && (dst_index + 2 < dst_end)) ? tmp_data.z : dst_data.z; - dst_data.w = ((dst_index + 3 >= dst_start) && (dst_index + 3 < dst_end)) ? tmp_data.w : dst_data.w; - - *((__global uchar4 *)(dst + dst_index)) = dst_data; - } -} -#endif From 799afab23b2ffe632a4379c2c8cb59aea6523466 Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Tue, 24 Sep 2013 14:02:01 +0400 Subject: [PATCH 46/68] refactored and extended ocl::exp and ocl::log --- modules/ocl/src/arithm.cpp | 41 +++++++++------- modules/ocl/src/opencl/arithm_exp.cl | 70 +++++++++++++++++----------- modules/ocl/src/opencl/arithm_log.cl | 63 +++++++++++++++---------- 3 files changed, 105 insertions(+), 69 deletions(-) diff --git a/modules/ocl/src/arithm.cpp b/modules/ocl/src/arithm.cpp index 1effac2138..97da8c08d1 100644 --- a/modules/ocl/src/arithm.cpp +++ b/modules/ocl/src/arithm.cpp @@ -817,39 +817,44 @@ void cv::ocl::LUT(const oclMat &src, const oclMat &lut, oclMat &dst) ////////////////////////////////////////////////////////////////////////////// //////////////////////////////// exp log ///////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// + static void arithmetic_exp_log_run(const oclMat &src, oclMat &dst, string kernelName, const char **kernelString) { - dst.create(src.size(), src.type()); - CV_Assert(src.cols == dst.cols && - src.rows == dst.rows ); - - CV_Assert(src.type() == dst.type()); - CV_Assert( src.type() == CV_32F || src.type() == CV_64F); - Context *clCxt = src.clCxt; - if(!clCxt->supportsFeature(Context::CL_DOUBLE) && src.type() == CV_64F) + if (!clCxt->supportsFeature(Context::CL_DOUBLE) && src.depth() == CV_64F) { - CV_Error(CV_GpuNotSupported, "Selected device don't support double\r\n"); + CV_Error(CV_GpuNotSupported, "Selected device doesn't support double\r\n"); return; } - //int channels = dst.oclchannels(); - int depth = dst.depth(); + + CV_Assert( src.depth() == CV_32F || src.depth() == CV_64F); + dst.create(src.size(), src.type()); + + int ddepth = dst.depth(); + int cols1 = src.cols * src.oclchannels(); + int srcoffset1 = src.offset / src.elemSize1(), dstoffset1 = dst.offset / dst.elemSize1(); + int srcstep1 = src.step1(), dststep1 = dst.step1(); size_t localThreads[3] = { 64, 4, 1 }; size_t globalThreads[3] = { dst.cols, dst.rows, 1 }; + std::string buildOptions = format("-D srcT=%s", + ddepth == CV_32F ? "float" : "double"); + vector > args; - args.push_back( make_pair( sizeof(cl_int), (void *)&src.rows )); - args.push_back( make_pair( sizeof(cl_int), (void *)&src.cols )); - args.push_back( make_pair( sizeof(cl_int), (void *)&src.step )); - args.push_back( make_pair( sizeof(cl_int), (void *)&dst.step )); - args.push_back( make_pair( sizeof(cl_int), (void *)&src.offset )); - args.push_back( make_pair( sizeof(cl_int), (void *)&dst.offset )); args.push_back( make_pair( sizeof(cl_mem), (void *)&src.data )); args.push_back( make_pair( sizeof(cl_mem), (void *)&dst.data )); + args.push_back( make_pair( sizeof(cl_int), (void *)&cols1 )); + args.push_back( make_pair( sizeof(cl_int), (void *)&src.rows )); + args.push_back( make_pair( sizeof(cl_int), (void *)&srcoffset1 )); + args.push_back( make_pair( sizeof(cl_int), (void *)&dstoffset1 )); + args.push_back( make_pair( sizeof(cl_int), (void *)&srcstep1 )); + args.push_back( make_pair( sizeof(cl_int), (void *)&dststep1 )); - openCLExecuteKernel(clCxt, kernelString, kernelName, globalThreads, localThreads, args, -1, depth); + openCLExecuteKernel(clCxt, kernelString, kernelName, globalThreads, localThreads, + args, src.oclchannels(), -1, buildOptions.c_str()); } + void cv::ocl::exp(const oclMat &src, oclMat &dst) { arithmetic_exp_log_run(src, dst, "arithm_exp", &arithm_exp); diff --git a/modules/ocl/src/opencl/arithm_exp.cl b/modules/ocl/src/opencl/arithm_exp.cl index 6f537a2870..b2143ba142 100644 --- a/modules/ocl/src/opencl/arithm_exp.cl +++ b/modules/ocl/src/opencl/arithm_exp.cl @@ -42,52 +42,70 @@ // the use of this software, even if advised of the possibility of such damage. // //M*/ + #if defined (DOUBLE_SUPPORT) +#ifdef cl_khr_fp64 #pragma OPENCL EXTENSION cl_khr_fp64:enable +#elif defined (cl_amd_fp64) +#pragma OPENCL EXTENSION cl_amd_fp64:enable +#endif #endif - ////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////EXP////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////// -__kernel void arithm_exp_D5(int rows, int cols, int srcStep, int dstStep, int srcOffset, int dstOffset, __global float *src, __global float *dst) +__kernel void arithm_exp_C1(__global srcT *src, __global srcT *dst, + int cols1, int rows, + int srcOffset1, int dstOffset1, + int srcStep1, int dstStep1) { - int x = get_global_id(0); int y = get_global_id(1); - if(x < cols && y < rows) + if(x < cols1 && y < rows) { - x = x << 2; - int srcIdx = mad24( y, srcStep, x + srcOffset); - int dstIdx = mad24( y, dstStep, x + dstOffset); + int srcIdx = mad24(y, srcStep1, x + srcOffset1); + int dstIdx = mad24(y, dstStep1, x + dstOffset1); - float src_data = *((__global float *)((__global char *)src + srcIdx)); - float dst_data = exp(src_data); + dst[dstIdx] = exp(src[srcIdx]); + } +} + +__kernel void arithm_exp_C2(__global srcT *src, __global srcT *dst, + int cols1, int rows, + int srcOffset1, int dstOffset1, + int srcStep1, int dstStep1) +{ + int x1 = get_global_id(0) << 1; + int y = get_global_id(1); - *((__global float *)((__global char *)dst + dstIdx)) = dst_data; + if(x1 < cols1 && y < rows) + { + int srcIdx = mad24(y, srcStep1, x1 + srcOffset1); + int dstIdx = mad24(y, dstStep1, x1 + dstOffset1); + dst[dstIdx] = exp(src[srcIdx]); + dst[dstIdx + 1] = x1 + 1 < cols1 ? exp(src[srcIdx + 1]) : dst[dstIdx + 1]; } } -#if defined (DOUBLE_SUPPORT) -__kernel void arithm_exp_D6(int rows, int cols, int srcStep, int dstStep, int srcOffset, int dstOffset, __global double *src, __global double *dst) +__kernel void arithm_exp_C4(__global srcT *src, __global srcT *dst, + int cols1, int rows, + int srcOffset1, int dstOffset1, + int srcStep1, int dstStep1) { - int x = get_global_id(0); - int y = get_global_id(1); - if(x < cols && y < rows ) - { - x = x << 3; - int srcIdx = mad24( y, srcStep, x + srcOffset); - int dstIdx = mad24( y, dstStep, x + dstOffset); + int x1 = get_global_id(0) << 2; + int y = get_global_id(1); - double src_data = *((__global double *)((__global char *)src + srcIdx)); - double dst_data = exp(src_data); + if(x1 < cols1 && y < rows) + { + int srcIdx = mad24(y, srcStep1, x1 + srcOffset1); + int dstIdx = mad24(y, dstStep1, x1 + dstOffset1); - *((__global double *)((__global char *)dst + dstIdx )) = dst_data; - // dst[dstIdx] = exp(src[srcIdx]); - } + dst[dstIdx] = exp(src[srcIdx]); + dst[dstIdx + 1] = x1 + 1 < cols1 ? exp(src[srcIdx + 1]) : dst[dstIdx + 1]; + dst[dstIdx + 2] = x1 + 2 < cols1 ? exp(src[srcIdx + 2]) : dst[dstIdx + 2]; + dst[dstIdx + 3] = x1 + 3 < cols1 ? exp(src[srcIdx + 3]) : dst[dstIdx + 3]; + } } - -#endif diff --git a/modules/ocl/src/opencl/arithm_log.cl b/modules/ocl/src/opencl/arithm_log.cl index ea19c9d902..ef8c4dd04e 100644 --- a/modules/ocl/src/opencl/arithm_log.cl +++ b/modules/ocl/src/opencl/arithm_log.cl @@ -1,4 +1,3 @@ - /*M/////////////////////////////////////////////////////////////////////////////////////// // // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. @@ -43,52 +42,66 @@ // the use of this software, even if advised of the possibility of such damage. // //M*/ + #if defined (DOUBLE_SUPPORT) #pragma OPENCL EXTENSION cl_khr_fp64:enable #endif -#define INF_FLOAT -88.029694 -#define INF_DOUBLE -709.0895657128241 - - ////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////LOG///////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////// -__kernel void arithm_log_D5(int rows, int cols, int srcStep, int dstStep, int srcOffset, int dstOffset, __global float *src, __global float *dst) +__kernel void arithm_log_C1(__global srcT *src, __global srcT *dst, + int cols1, int rows, + int srcOffset1, int dstOffset1, + int srcStep1, int dstStep1) { int x = get_global_id(0); int y = get_global_id(1); - if(x < cols && y < rows ) + if(x < cols1 && y < rows) { - x = x << 2; - int srcIdx = mad24( y, srcStep, x + srcOffset); - int dstIdx = mad24( y, dstStep, x + dstOffset); + int srcIdx = mad24(y, srcStep1, x + srcOffset1); + int dstIdx = mad24(y, dstStep1, x + dstOffset1); - float src_data = *((__global float *)((__global char *)src + srcIdx)); - float dst_data = (src_data == 0) ? INF_FLOAT : log(fabs(src_data)); - - *((__global float *)((__global char *)dst + dstIdx)) = dst_data; + dst[dstIdx] = log(src[srcIdx]); } } -#if defined (DOUBLE_SUPPORT) -__kernel void arithm_log_D6(int rows, int cols, int srcStep, int dstStep, int srcOffset, int dstOffset, __global double *src, __global double *dst) +__kernel void arithm_log_C2(__global srcT *src, __global srcT *dst, + int cols1, int rows, + int srcOffset1, int dstOffset1, + int srcStep1, int dstStep1) { - int x = get_global_id(0); + int x1 = get_global_id(0) << 1; int y = get_global_id(1); - if(x < cols && y < rows ) + if(x1 < cols1 && y < rows) { - x = x << 3; - int srcIdx = mad24( y, srcStep, x + srcOffset); - int dstIdx = mad24( y, dstStep, x + dstOffset); + int srcIdx = mad24(y, srcStep1, x1 + srcOffset1); + int dstIdx = mad24(y, dstStep1, x1 + dstOffset1); - double src_data = *((__global double *)((__global char *)src + srcIdx)); - double dst_data = (src_data == 0) ? INF_DOUBLE : log(fabs(src_data)); - *((__global double *)((__global char *)dst + dstIdx)) = dst_data; + dst[dstIdx] = log(src[srcIdx]); + dst[dstIdx + 1] = x1 + 1 < cols1 ? log(src[srcIdx + 1]) : dst[dstIdx + 1]; + } +} + +__kernel void arithm_log_C4(__global srcT *src, __global srcT *dst, + int cols1, int rows, + int srcOffset1, int dstOffset1, + int srcStep1, int dstStep1) +{ + int x1 = get_global_id(0) << 2; + int y = get_global_id(1); + if(x1 < cols1 && y < rows) + { + int srcIdx = mad24(y, srcStep1, x1 + srcOffset1); + int dstIdx = mad24(y, dstStep1, x1 + dstOffset1); + + dst[dstIdx] = log(src[srcIdx]); + dst[dstIdx + 1] = x1 + 1 < cols1 ? log(src[srcIdx + 1]) : dst[dstIdx + 1]; + dst[dstIdx + 2] = x1 + 2 < cols1 ? log(src[srcIdx + 2]) : dst[dstIdx + 2]; + dst[dstIdx + 3] = x1 + 3 < cols1 ? log(src[srcIdx + 3]) : dst[dstIdx + 3]; } } -#endif From b4ad12821861c3527ed06755b0959ebbeab956b9 Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Tue, 24 Sep 2013 14:07:54 +0400 Subject: [PATCH 47/68] refactoed and extended ocl::transpose --- modules/ocl/src/arithm.cpp | 72 ++-- modules/ocl/src/opencl/arithm_transpose.cl | 478 ++------------------- 2 files changed, 66 insertions(+), 484 deletions(-) diff --git a/modules/ocl/src/arithm.cpp b/modules/ocl/src/arithm.cpp index 97da8c08d1..b18fa44dc9 100644 --- a/modules/ocl/src/arithm.cpp +++ b/modules/ocl/src/arithm.cpp @@ -1511,57 +1511,51 @@ oclMatExpr::operator oclMat() const ////////////////////////////////////////////////////////////////////////////// /////////////////////////////// transpose //////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// + #define TILE_DIM (32) #define BLOCK_ROWS (256/TILE_DIM) + static void transpose_run(const oclMat &src, oclMat &dst, string kernelName) { - if(!src.clCxt->supportsFeature(Context::CL_DOUBLE) && src.type() == CV_64F) + Context *clCxt = src.clCxt; + if (!clCxt->supportsFeature(Context::CL_DOUBLE) && src.depth() == CV_64F) { - CV_Error(CV_GpuNotSupported, "Selected device don't support double\r\n"); + CV_Error(CV_GpuNotSupported, "Selected device doesn't support double\r\n"); return; } - CV_Assert(src.cols == dst.rows && src.rows == dst.cols); - - Context *clCxt = src.clCxt; - int channels = src.oclchannels(); - int depth = src.depth(); - - int vector_lengths[4][7] = {{1, 0, 0, 0, 1, 1, 0}, - {0, 0, 1, 1, 0, 0, 0}, - {0, 0, 0, 0 , 0, 0, 0}, - {1, 1, 0, 0, 0, 0, 0} - }; - - size_t vector_length = vector_lengths[channels - 1][depth]; - int offset_cols = ((dst.offset % dst.step) / dst.elemSize()) & (vector_length - 1); - int cols = divUp(src.cols + offset_cols, vector_length); + const char * const typeMap[] = { "uchar", "char", "ushort", "short", "int", "float", "double" }; + const char channelsString[] = { ' ', ' ', '2', '4', '4' }; + std::string buildOptions = format("-D T=%s%c", typeMap[src.depth()], + channelsString[src.channels()]); size_t localThreads[3] = { TILE_DIM, BLOCK_ROWS, 1 }; - size_t globalThreads[3] = { cols, src.rows, 1 }; + size_t globalThreads[3] = { src.cols, src.rows, 1 }; + + int srcstep1 = src.step / src.elemSize(), dststep1 = dst.step / dst.elemSize(); + int srcoffset1 = src.offset / src.elemSize(), dstoffset1 = dst.offset / dst.elemSize(); vector > args; args.push_back( make_pair( sizeof(cl_mem), (void *)&src.data )); - args.push_back( make_pair( sizeof(cl_int), (void *)&src.step )); - args.push_back( make_pair( sizeof(cl_int), (void *)&src.offset )); args.push_back( make_pair( sizeof(cl_mem), (void *)&dst.data )); - args.push_back( make_pair( sizeof(cl_int), (void *)&dst.step )); - args.push_back( make_pair( sizeof(cl_int), (void *)&dst.offset )); + args.push_back( make_pair( sizeof(cl_int), (void *)&src.cols )); args.push_back( make_pair( sizeof(cl_int), (void *)&src.rows )); - args.push_back( make_pair( sizeof(cl_int), (void *)&cols )); + args.push_back( make_pair( sizeof(cl_int), (void *)&srcstep1 )); + args.push_back( make_pair( sizeof(cl_int), (void *)&dststep1 )); + args.push_back( make_pair( sizeof(cl_int), (void *)&srcoffset1 )); + args.push_back( make_pair( sizeof(cl_int), (void *)&dstoffset1 )); - openCLExecuteKernel(clCxt, &arithm_transpose, kernelName, globalThreads, localThreads, args, channels, depth); + openCLExecuteKernel(clCxt, &arithm_transpose, kernelName, globalThreads, localThreads, + args, -1, -1, buildOptions.c_str()); } void cv::ocl::transpose(const oclMat &src, oclMat &dst) { - CV_Assert(src.type() == CV_8UC1 || src.type() == CV_8UC3 || src.type() == CV_8UC4 || src.type() == CV_8SC3 || src.type() == CV_8SC4 || - src.type() == CV_16UC2 || src.type() == CV_16SC2 || src.type() == CV_32SC1 || src.type() == CV_32FC1); - - oclMat emptyMat; + CV_Assert(src.depth() <= CV_64F && src.channels() <= 4); - if( src.data == dst.data && dst.cols == dst.rows ) - transpose_run( src, emptyMat, "transposeI_"); + if ( src.data == dst.data && src.cols == src.rows && dst.offset == src.offset + && dst.rows == dst.cols && src.cols == dst.cols) + transpose_run( src, dst, "transpose_inplace"); else { dst.create(src.cols, src.rows, src.type()); @@ -1569,6 +1563,10 @@ void cv::ocl::transpose(const oclMat &src, oclMat &dst) } } +////////////////////////////////////////////////////////////////////////////// +////////////////////////////// addWeighted /////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + void cv::ocl::addWeighted(const oclMat &src1, double alpha, const oclMat &src2, double beta, double gama, oclMat &dst) { Context *clCxt = src1.clCxt; @@ -1633,6 +1631,10 @@ void cv::ocl::addWeighted(const oclMat &src1, double alpha, const oclMat &src2, args, -1, -1, buildOptions.c_str()); } +////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////// Pow ////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + static void arithmetic_pow_run(const oclMat &src1, double p, oclMat &dst, string kernelName, const char **kernelString) { CV_Assert(src1.cols == dst.cols && src1.rows == dst.rows); @@ -1671,6 +1673,7 @@ static void arithmetic_pow_run(const oclMat &src1, double p, oclMat &dst, string openCLExecuteKernel(clCxt, kernelString, kernelName, globalThreads, localThreads, args, -1, depth); } + void cv::ocl::pow(const oclMat &x, double p, oclMat &y) { if(!x.clCxt->supportsFeature(Context::CL_DOUBLE) && x.type() == CV_64F) @@ -1685,6 +1688,11 @@ void cv::ocl::pow(const oclMat &x, double p, oclMat &y) arithmetic_pow_run(x, p, y, kernelName, &arithm_pow); } + +////////////////////////////////////////////////////////////////////////////// +/////////////////////////////// setIdentity ////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + void cv::ocl::setIdentity(oclMat& src, double scalar) { CV_Assert(src.empty() == false && src.rows == src.cols); @@ -1711,7 +1719,6 @@ void cv::ocl::setIdentity(oclMat& src, double scalar) } - vector > args; args.push_back( make_pair( sizeof(cl_mem), (void *)&src.data )); args.push_back( make_pair( sizeof(cl_int), (void *)&src.rows)); @@ -1735,7 +1742,8 @@ void cv::ocl::setIdentity(oclMat& src, double scalar) { scalar_i = (int)scalar; args.push_back(make_pair(sizeof(cl_int), (void*)&scalar_i)); - }else + } + else { scalar_f = (float)scalar; args.push_back(make_pair(sizeof(cl_float), (void*)&scalar_f)); diff --git a/modules/ocl/src/opencl/arithm_transpose.cl b/modules/ocl/src/opencl/arithm_transpose.cl index d0725b0175..57f7f1b9d3 100644 --- a/modules/ocl/src/opencl/arithm_transpose.cl +++ b/modules/ocl/src/opencl/arithm_transpose.cl @@ -43,468 +43,42 @@ // //M*/ -#define TILE_DIM 32 -#define BLOCK_ROWS 8 -#define LDS_STEP (TILE_DIM + 1) - - -//8UC1 is not unoptimized, as the size of write per thread is 8 -//which will use completepath -__kernel void transpose_C1_D0(__global uchar* src, int src_step, int src_offset, - __global uchar* dst, int dst_step, int dst_offset, - int src_rows, int src_cols) -{ - - int gp_x = get_group_id(0), gp_y = get_group_id(1); - int gs_x = get_num_groups(0), gs_y = get_num_groups(1); - - int groupId_x, groupId_y; - - if(src_rows == src_cols) - { - groupId_y = gp_x; - groupId_x = (gp_x + gp_y) % gs_x; - } - else - { - int bid = gp_x + gs_x * gp_y; - groupId_y = bid % gs_y; - groupId_x = ((bid / gs_y) + groupId_y) % gs_x; - } - - int lx = get_local_id(0); - int ly = get_local_id(1); - - int x = groupId_x * TILE_DIM + lx; - int y = groupId_y * TILE_DIM + ly; - - int x_index = groupId_y * TILE_DIM + lx; - int y_index = groupId_x * TILE_DIM + ly; - - __local uchar title[TILE_DIM * LDS_STEP]; - - if(x < src_cols && y < src_rows) - { - int index_src = mad24(y, src_step, x); - - #pragma unroll - for(int i = 0; i < TILE_DIM; i += BLOCK_ROWS) - { - if(y + i < src_rows) - { - title[(ly + i) * LDS_STEP + lx] =*(src + src_offset + index_src); - index_src = mad24(BLOCK_ROWS, src_step, index_src); - } - } - } - - barrier(CLK_LOCAL_MEM_FENCE); - - if(x_index < src_rows && y_index < src_cols) - { - int index_dst = mad24(y_index, dst_step, x_index); - - #pragma unroll - for(int i = 0; i < TILE_DIM; i += BLOCK_ROWS) - { - if((y_index + i) < src_cols) - { - *(dst + dst_offset + index_dst ) = title[lx * LDS_STEP + ly + i]; - index_dst += dst_step * BLOCK_ROWS ; - } - } - } -} - -__kernel void transpose_C1_D4(__global int* src, int src_step, int src_offset, - __global int* dst, int dst_step, int dst_offset, - int src_rows, int src_cols) -{ - - int gp_x = get_group_id(0), gp_y = get_group_id(1); - int gs_x = get_num_groups(0), gs_y = get_num_groups(1); - - int groupId_x, groupId_y; - - if(src_rows == src_cols) - { - groupId_y = gp_x; - groupId_x = (gp_x + gp_y) % gs_x; - } - else - { - int bid = gp_x + gs_x * gp_y; - groupId_y = bid % gs_y; - groupId_x = ((bid / gs_y) + groupId_y) % gs_x; - } - - int lx = get_local_id(0); - int ly = get_local_id(1); - - int x = groupId_x * TILE_DIM + lx; - int y = groupId_y * TILE_DIM + ly; - - int x_index = groupId_y * TILE_DIM + lx; - int y_index = groupId_x * TILE_DIM + ly; - - __local int title[TILE_DIM * LDS_STEP]; - - if(x < src_cols && y < src_rows) - { - int index_src = mad24(y, src_step, (x << 2)); - - #pragma unroll - for(int i = 0; i < TILE_DIM; i += BLOCK_ROWS) - { - if(y + i < src_rows) - { - title[(ly + i) * LDS_STEP + lx] = *((__global int *)((__global char*)src + src_offset + index_src)); - index_src = mad24(BLOCK_ROWS, src_step, index_src); - } - } - } - - barrier(CLK_LOCAL_MEM_FENCE); - - if(x_index < src_rows && y_index < src_cols) - { - int index_dst = mad24(y_index, dst_step, (x_index << 2)); - - #pragma unroll - for(int i = 0; i < TILE_DIM; i += BLOCK_ROWS) - { - if((y_index + i) < src_cols) - { - *((__global int*)((__global char*)dst + dst_offset + index_dst )) = title[lx * LDS_STEP + ly + i]; - index_dst += dst_step * BLOCK_ROWS ; - } - } - } -} -__kernel void transpose_C1_D5(__global float* src, int src_step, int src_offset, - __global float* dst, int dst_step, int dst_offset, - int src_rows, int src_cols) +#if defined (DOUBLE_SUPPORT) +#pragma OPENCL EXTENSION cl_khr_fp64:enable +#endif + +__kernel void transpose(__global const T* src, __global T* dst, + int src_cols, int src_rows, + int src_step, int dst_step, + int src_offset, int dst_offset) { + int x = get_global_id(0); + int y = get_global_id(1); - int gp_x = get_group_id(0), gp_y = get_group_id(1); - int gs_x = get_num_groups(0), gs_y = get_num_groups(1); - - int groupId_x, groupId_y; - - if(src_rows == src_cols) - { - groupId_y = gp_x; - groupId_x = (gp_x + gp_y) % gs_x; - } - else + if (x < src_cols && y < src_rows) { - int bid = gp_x + gs_x * gp_y; - groupId_y = bid % gs_y; - groupId_x = ((bid / gs_y) + groupId_y) % gs_x; - } - - int lx = get_local_id(0); - int ly = get_local_id(1); - - int x = groupId_x * TILE_DIM + lx; - int y = groupId_y * TILE_DIM + ly; - - int x_index = groupId_y * TILE_DIM + lx; - int y_index = groupId_x * TILE_DIM + ly; + int srcIdx = mad24(y, src_step, src_offset + x); + int dstIdx = mad24(x, dst_step, dst_offset + y); - __local float title[TILE_DIM * LDS_STEP]; - - if(x < src_cols && y < src_rows) - { - int index_src = mad24(y, src_step, (x << 2)); - - #pragma unroll - for(int i = 0; i < TILE_DIM; i += BLOCK_ROWS) - { - if(y + i < src_rows) - { - title[(ly + i) * LDS_STEP + lx] = *((__global float *)((__global char*)src + src_offset + index_src)); - index_src = mad24(BLOCK_ROWS, src_step, index_src); - } - } - } - - barrier(CLK_LOCAL_MEM_FENCE); - - if(x_index < src_rows && y_index < src_cols) - { - int index_dst = mad24(y_index, dst_step, (x_index << 2)); - - #pragma unroll - for(int i = 0; i < TILE_DIM; i += BLOCK_ROWS) - { - if((y_index + i) < src_cols) - { - *((__global float*)((__global char*)dst + dst_offset + index_dst )) = title[lx * LDS_STEP + ly + i]; - index_dst += dst_step * BLOCK_ROWS ; - } - } + dst[dstIdx] = src[srcIdx]; } } -__kernel void transpose_C2_D2(__global ushort* src, int src_step, int src_offset, - __global ushort* dst, int dst_step, int dst_offset, - int src_rows, int src_cols) +__kernel void transpose_inplace(__global T* src, __global T* dst, + int src_cols, int src_rows, + int src_step, int dst_step, + int src_offset, int dst_offset) { + int x = get_global_id(0); + int y = get_global_id(1); - int gp_x = get_group_id(0), gp_y = get_group_id(1); - int gs_x = get_num_groups(0), gs_y = get_num_groups(1); - - int groupId_x, groupId_y; - - if(src_rows == src_cols) - { - groupId_y = gp_x; - groupId_x = (gp_x + gp_y) % gs_x; - } - else - { - int bid = gp_x + gs_x * gp_y; - groupId_y = bid % gs_y; - groupId_x = ((bid / gs_y) + groupId_y) % gs_x; - } - - int lx = get_local_id(0); - int ly = get_local_id(1); - - int x = groupId_x * TILE_DIM + lx; - int y = groupId_y * TILE_DIM + ly; - - int x_index = groupId_y * TILE_DIM + lx; - int y_index = groupId_x * TILE_DIM + ly; - - __local ushort2 title[TILE_DIM * LDS_STEP]; - - if(x < src_cols && y < src_rows) - { - int index_src = mad24(y, src_step, (x << 2)); - - #pragma unroll - for(int i = 0; i < TILE_DIM; i += BLOCK_ROWS) - { - if(y + i < src_rows) - { - title[(ly + i) * LDS_STEP + lx] = *((__global ushort2 *)((__global char*)src + src_offset + index_src)); - index_src = mad24(BLOCK_ROWS, src_step, index_src); - } - } - } - - barrier(CLK_LOCAL_MEM_FENCE); - - if(x_index < src_rows && y_index < src_cols) - { - int index_dst = mad24(y_index, dst_step, (x_index << 2)); - - #pragma unroll - for(int i = 0; i < TILE_DIM; i += BLOCK_ROWS) - { - if((y_index + i) < src_cols) - { - *((__global ushort2*)((__global char*)dst + dst_offset + index_dst )) = title[lx * LDS_STEP + ly + i]; - index_dst += dst_step * BLOCK_ROWS ; - } - } - } -} -__kernel void transpose_C2_D3(__global short* src, int src_step, int src_offset, - __global short* dst, int dst_step, int dst_offset, - int src_rows, int src_cols) -{ - - int gp_x = get_group_id(0), gp_y = get_group_id(1); - int gs_x = get_num_groups(0), gs_y = get_num_groups(1); - - int groupId_x, groupId_y; - - if(src_rows == src_cols) - { - groupId_y = gp_x; - groupId_x = (gp_x + gp_y) % gs_x; - } - else - { - int bid = gp_x + gs_x * gp_y; - groupId_y = bid % gs_y; - groupId_x = ((bid / gs_y) + groupId_y) % gs_x; - } - - int lx = get_local_id(0); - int ly = get_local_id(1); - - int x = groupId_x * TILE_DIM + lx; - int y = groupId_y * TILE_DIM + ly; - - int x_index = groupId_y * TILE_DIM + lx; - int y_index = groupId_x * TILE_DIM + ly; - - __local short2 title[TILE_DIM * LDS_STEP]; - - if(x < src_cols && y < src_rows) - { - int index_src = mad24(y, src_step, (x << 2)); - - #pragma unroll - for(int i = 0; i < TILE_DIM; i += BLOCK_ROWS) - { - if(y + i < src_rows) - { - title[(ly + i) * LDS_STEP + lx] = *((__global short2 *)((__global char*)src + src_offset + index_src)); - index_src = mad24(BLOCK_ROWS, src_step, index_src); - } - } - } - - barrier(CLK_LOCAL_MEM_FENCE); - - if(x_index < src_rows && y_index < src_cols) - { - int index_dst = mad24(y_index, dst_step, (x_index << 2)); - - #pragma unroll - for(int i = 0; i < TILE_DIM; i += BLOCK_ROWS) - { - if((y_index + i) < src_cols) - { - *((__global short2*)((__global char*)dst + dst_offset + index_dst )) = title[lx * LDS_STEP + ly + i]; - index_dst += dst_step * BLOCK_ROWS ; - } - } - } -} -__kernel void transpose_C4_D0(__global uchar* src, int src_step, int src_offset, - __global uchar* dst, int dst_step, int dst_offset, - int src_rows, int src_cols) -{ - - int gp_x = get_group_id(0), gp_y = get_group_id(1); - int gs_x = get_num_groups(0), gs_y = get_num_groups(1); - - int groupId_x, groupId_y; - - if(src_rows == src_cols) - { - groupId_y = gp_x; - groupId_x = (gp_x + gp_y) % gs_x; - } - else - { - int bid = gp_x + gs_x * gp_y; - groupId_y = bid % gs_y; - groupId_x = ((bid / gs_y) + groupId_y) % gs_x; - } - - int lx = get_local_id(0); - int ly = get_local_id(1); - - int x = groupId_x * TILE_DIM + lx; - int y = groupId_y * TILE_DIM + ly; - - int x_index = groupId_y * TILE_DIM + lx; - int y_index = groupId_x * TILE_DIM + ly; - - __local uchar4 title[TILE_DIM * LDS_STEP]; - - if(x < src_cols && y < src_rows) - { - int index_src = mad24(y, src_step, (x << 2)); - - #pragma unroll - for(int i = 0; i < TILE_DIM; i += BLOCK_ROWS) - { - if(y + i < src_rows) - { - title[(ly + i) * LDS_STEP + lx] = *((__global uchar4 *)(src + src_offset + index_src)); - index_src = mad24(BLOCK_ROWS, src_step, index_src); - } - } - } - - barrier(CLK_LOCAL_MEM_FENCE); - - if(x_index < src_rows && y_index < src_cols) - { - int index_dst = mad24(y_index, dst_step, (x_index << 2)); - - #pragma unroll - for(int i = 0; i < TILE_DIM; i += BLOCK_ROWS) - { - if((y_index + i) < src_cols) - { - *((__global uchar4*)(dst + dst_offset + index_dst )) = title[lx * LDS_STEP + ly + i]; - index_dst += dst_step * BLOCK_ROWS ; - } - } - } -} - -__kernel void transpose_C4_D1(__global char* src, int src_step, int src_offset, - __global char* dst, int dst_step, int dst_offset, - int src_rows, int src_cols) -{ - - int gp_x = get_group_id(0), gp_y = get_group_id(1); - int gs_x = get_num_groups(0), gs_y = get_num_groups(1); - - int groupId_x, groupId_y; - - if(src_rows == src_cols) - { - groupId_y = gp_x; - groupId_x = (gp_x + gp_y) % gs_x; - } - else - { - int bid = gp_x + gs_x * gp_y; - groupId_y = bid % gs_y; - groupId_x = ((bid / gs_y) + groupId_y) % gs_x; - } - - int lx = get_local_id(0); - int ly = get_local_id(1); - - int x = groupId_x * TILE_DIM + lx; - int y = groupId_y * TILE_DIM + ly; - - int x_index = groupId_y * TILE_DIM + lx; - int y_index = groupId_x * TILE_DIM + ly; - - __local char4 title[TILE_DIM * LDS_STEP]; - - if(x < src_cols && y < src_rows) - { - int index_src = mad24(y, src_step, (x << 2)); - - #pragma unroll - for(int i = 0; i < TILE_DIM; i += BLOCK_ROWS) - { - if(y + i < src_rows) - { - title[(ly + i) * LDS_STEP + lx] = *((__global char4 *)(src + src_offset + index_src)); - index_src = mad24(BLOCK_ROWS, src_step, index_src); - } - } - } - - barrier(CLK_LOCAL_MEM_FENCE); - - if(x_index < src_rows && y_index < src_cols) + if (x < src_cols && y < src_rows && x < y) { - int index_dst = mad24(y_index, dst_step, (x_index << 2)); + int srcIdx = mad24(y, src_step, src_offset + x); + int dstIdx = mad24(x, dst_step, dst_offset + y); - #pragma unroll - for(int i = 0; i < TILE_DIM; i += BLOCK_ROWS) - { - if((y_index + i) < src_cols) - { - *((__global char4*)(dst + dst_offset + index_dst )) = title[lx * LDS_STEP + ly + i]; - index_dst += dst_step * BLOCK_ROWS ; - } - } + T tmp = dst[dstIdx]; + dst[dstIdx] = src[srcIdx]; + src[srcIdx] = tmp; } } From 8aa3eb817d4a40f418b3a7691960ec40904ecd99 Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Tue, 24 Sep 2013 14:13:10 +0400 Subject: [PATCH 48/68] refactored arithm.cpp. Extended param list for all the tests --- modules/ocl/src/arithm.cpp | 46 +- modules/ocl/test/test_arithm.cpp | 1177 ++++++++++++++++-------------- 2 files changed, 640 insertions(+), 583 deletions(-) diff --git a/modules/ocl/src/arithm.cpp b/modules/ocl/src/arithm.cpp index b18fa44dc9..035cea781e 100644 --- a/modules/ocl/src/arithm.cpp +++ b/modules/ocl/src/arithm.cpp @@ -97,9 +97,6 @@ namespace cv extern const char *arithm_setidentity; } } -////////////////////////////////////////////////////////////////////////////// -/////////////////////// add subtract multiply divide ///////////////////////// -////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// /////////////////////// add subtract multiply divide ///////////////////////// @@ -251,7 +248,7 @@ void cv::ocl::divide(double scalar, const oclMat &src, oclMat &dst) } ////////////////////////////////////////////////////////////////////////////// -///////////////////////////////// Absdiff /////////////////////////////////// +///////////////////////////////// Absdiff //////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// void cv::ocl::absdiff(const oclMat &src1, const oclMat &src2, oclMat &dst) @@ -430,9 +427,11 @@ Scalar cv::ocl::sqrSum(const oclMat &src) func = functab[(int)src.clCxt->supportsFeature(Context::CL_DOUBLE)]; return func(src, 2); } + ////////////////////////////////////////////////////////////////////////////// //////////////////////////////// meanStdDev ////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// + void cv::ocl::meanStdDev(const oclMat &src, Scalar &mean, Scalar &stddev) { CV_Assert(src.depth() <= CV_32S); @@ -441,8 +440,10 @@ void cv::ocl::meanStdDev(const oclMat &src, Scalar &mean, Scalar &stddev) Mat m1(sz, CV_MAKETYPE(CV_32S, channels), cv::Scalar::all(0)), m2(sz, CV_MAKETYPE(CV_32S, channels), cv::Scalar::all(0)); oclMat dst1(m1), dst2(m2); + //arithmetic_sum_run(src, dst1,"arithm_op_sum"); //arithmetic_sum_run(src, dst2,"arithm_op_squares_sum"); + m1 = (Mat)dst1; m2 = (Mat)dst2; int i = 0, *p = (int *)m1.data, *q = (int *)m2.data; @@ -456,6 +457,7 @@ void cv::ocl::meanStdDev(const oclMat &src, Scalar &mean, Scalar &stddev) ////////////////////////////////////////////////////////////////////////////// //////////////////////////////////// minMax ///////////////////////////////// ////////////////////////////////////////////////////////////////////////////// + static void arithmetic_minMax_run(const oclMat &src, const oclMat &mask, cl_mem &dst, int vlen , int groupnum, string kernelName) { vector > args; @@ -572,6 +574,7 @@ void cv::ocl::minMax(const oclMat &src, double *minVal, double *maxVal, const oc oclMat buf; minMax_buf(src, minVal, maxVal, mask, buf); } + void cv::ocl::minMax_buf(const oclMat &src, double *minVal, double *maxVal, const oclMat &mask, oclMat &buf) { CV_Assert(src.oclchannels() == 1); @@ -598,6 +601,7 @@ void cv::ocl::minMax_buf(const oclMat &src, double *minVal, double *maxVal, cons ////////////////////////////////////////////////////////////////////////////// /////////////////////////////////// norm ///////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// + double cv::ocl::norm(const oclMat &src1, int normType) { return norm(src1, oclMat(src1.size(), src1.type(), Scalar::all(0)), normType); @@ -657,6 +661,7 @@ double cv::ocl::norm(const oclMat &src1, const oclMat &src2, int normType) ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////// flip ////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// + static void arithmetic_flip_rows_run(const oclMat &src, oclMat &dst, string kernelName) { if(!src.clCxt->supportsFeature(Context::CL_DOUBLE) && src.type() == CV_64F) @@ -703,6 +708,7 @@ static void arithmetic_flip_rows_run(const oclMat &src, oclMat &dst, string kern openCLExecuteKernel(clCxt, &arithm_flip, kernelName, globalThreads, localThreads, args, -1, depth); } + static void arithmetic_flip_cols_run(const oclMat &src, oclMat &dst, string kernelName, bool isVertical) { if(!src.clCxt->supportsFeature(Context::CL_DOUBLE) && src.type() == CV_64F) @@ -755,6 +761,7 @@ static void arithmetic_flip_cols_run(const oclMat &src, oclMat &dst, string kern openCLExecuteKernel(clCxt, kernelString, kernelName, globalThreads, localThreads, args, src.oclchannels(), depth); } + void cv::ocl::flip(const oclMat &src, oclMat &dst, int flipCode) { dst.create(src.size(), src.type()); @@ -868,6 +875,7 @@ void cv::ocl::log(const oclMat &src, oclMat &dst) ////////////////////////////////////////////////////////////////////////////// ////////////////////////////// magnitude phase /////////////////////////////// ////////////////////////////////////////////////////////////////////////////// + static void arithmetic_magnitude_phase_run(const oclMat &src1, const oclMat &src2, oclMat &dst, string kernelName) { if(!src1.clCxt->supportsFeature(Context::CL_DOUBLE) && src1.type() == CV_64F) @@ -951,6 +959,7 @@ static void arithmetic_phase_run(const oclMat &src1, const oclMat &src2, oclMat openCLExecuteKernel(clCxt, kernelString, kernelName, globalThreads, localThreads, args, -1, depth); } + void cv::ocl::phase(const oclMat &x, const oclMat &y, oclMat &Angle , bool angleInDegrees) { CV_Assert(x.type() == y.type() && x.size() == y.size() && (x.depth() == CV_32F || x.depth() == CV_64F)); @@ -965,6 +974,7 @@ void cv::ocl::phase(const oclMat &x, const oclMat &y, oclMat &Angle , bool angle ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////// cartToPolar /////////////////////////////// ////////////////////////////////////////////////////////////////////////////// + static void arithmetic_cartToPolar_run(const oclMat &src1, const oclMat &src2, oclMat &dst_mag, oclMat &dst_cart, string kernelName, bool angleInDegrees) { @@ -1003,6 +1013,7 @@ static void arithmetic_cartToPolar_run(const oclMat &src1, const oclMat &src2, o openCLExecuteKernel(clCxt, &arithm_cartToPolar, kernelName, globalThreads, localThreads, args, -1, depth); } + void cv::ocl::cartToPolar(const oclMat &x, const oclMat &y, oclMat &mag, oclMat &angle, bool angleInDegrees) { CV_Assert(x.type() == y.type() && x.size() == y.size() && (x.depth() == CV_32F || x.depth() == CV_64F)); @@ -1016,6 +1027,7 @@ void cv::ocl::cartToPolar(const oclMat &x, const oclMat &y, oclMat &mag, oclMat ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////// polarToCart /////////////////////////////// ////////////////////////////////////////////////////////////////////////////// + static void arithmetic_ptc_run(const oclMat &src1, const oclMat &src2, oclMat &dst1, oclMat &dst2, bool angleInDegrees, string kernelName) { @@ -1078,6 +1090,7 @@ void cv::ocl::polarToCart(const oclMat &magnitude, const oclMat &angle, oclMat & ////////////////////////////////////////////////////////////////////////////// /////////////////////////////////// minMaxLoc //////////////////////////////// ////////////////////////////////////////////////////////////////////////////// + static void arithmetic_minMaxLoc_run(const oclMat &src, cl_mem &dst, int vlen , int groupnum) { vector > args; @@ -1127,8 +1140,7 @@ static void arithmetic_minMaxLoc_mask_run(const oclMat &src, const oclMat &mask, args.push_back( make_pair( sizeof(cl_int) , (void *)&moffset )); args.push_back( make_pair( sizeof(cl_mem) , (void *)&mask.data )); args.push_back( make_pair( sizeof(cl_mem) , (void *)&dst )); - // printf("elemnum:%d,cols:%d,invalid_cols:%d,offset:%d,minvalid_cols:%d,moffset:%d,repeat_e:%d\r\n", - // elemnum,cols,invalid_cols,offset,minvalid_cols,moffset,repeat_me); + openCLExecuteKernel(src.clCxt, &arithm_minMaxLoc_mask, "arithm_op_minMaxLoc_mask", gt, lt, args, -1, -1, build_options); } } @@ -1144,14 +1156,12 @@ void arithmetic_minMaxLoc(const oclMat &src, double *minVal, double *maxVal, Context *clCxt = src.clCxt; cl_mem dstBuffer = openCLCreateBuffer(clCxt, CL_MEM_WRITE_ONLY, dbsize); *minVal = std::numeric_limits::max() , *maxVal = -std::numeric_limits::max(); + if (mask.empty()) - { arithmetic_minMaxLoc_run(src, dstBuffer, vlen, groupnum); - } else - { arithmetic_minMaxLoc_mask_run(src, mask, dstBuffer, vlen, groupnum); - } + T *p = new T[groupnum * vlen * 4]; memset(p, 0, dbsize); openCLReadBuffer(clCxt, dstBuffer, (void *)p, dbsize); @@ -1190,18 +1200,22 @@ void arithmetic_minMaxLoc(const oclMat &src, double *minVal, double *maxVal, maxLoc->x = maxLoc->y = -1; } delete[] p; + openCLSafeCall(clReleaseMemObject(dstBuffer)); } typedef void (*minMaxLocFunc)(const oclMat &src, double *minVal, double *maxVal, Point *minLoc, Point *maxLoc, const oclMat &mask); + void cv::ocl::minMaxLoc(const oclMat &src, double *minVal, double *maxVal, Point *minLoc, Point *maxLoc, const oclMat &mask) { if(!src.clCxt->supportsFeature(Context::CL_DOUBLE) && src.depth() == CV_64F) { CV_Error(CV_GpuNotSupported, "select device don't support double"); + return; } + static minMaxLocFunc functab[2] = { arithmetic_minMaxLoc, @@ -1216,6 +1230,7 @@ void cv::ocl::minMaxLoc(const oclMat &src, double *minVal, double *maxVal, ////////////////////////////////////////////////////////////////////////////// ///////////////////////////// countNonZero /////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// + static void arithmetic_countNonZero_run(const oclMat &src, cl_mem &dst, int vlen , int groupnum, string kernelName) { vector > args; @@ -1262,9 +1277,8 @@ int cv::ocl::countNonZero(const oclMat &src) memset(p, 0, dbsize * sizeof(int)); openCLReadBuffer(clCxt, dstBuffer, (void *)p, dbsize * sizeof(int)); for(int i = 0; i < dbsize; i++) - { nonzero += p[i]; - } + delete[] p; openCLSafeCall(clReleaseMemObject(dstBuffer)); return nonzero; @@ -1663,11 +1677,10 @@ static void arithmetic_pow_run(const oclMat &src1, double p, oclMat &dst, string args.push_back( make_pair( sizeof(cl_int), (void *)&dst.rows )); args.push_back( make_pair( sizeof(cl_int), (void *)&cols )); args.push_back( make_pair( sizeof(cl_int), (void *)&dst_step1 )); - float pf = p; + + float pf = static_cast(p); if(!src1.clCxt->supportsFeature(Context::CL_DOUBLE)) - { args.push_back( make_pair( sizeof(cl_float), (void *)&pf )); - } else args.push_back( make_pair( sizeof(cl_double), (void *)&p )); @@ -1733,7 +1746,8 @@ void cv::ocl::setIdentity(oclMat& src, double scalar) { scalar_i = (int)scalar; args.push_back(make_pair(sizeof(cl_int), (void*)&scalar_i)); - }else + } + else args.push_back(make_pair(sizeof(cl_double), (void*)&scalar)); } else diff --git a/modules/ocl/test/test_arithm.cpp b/modules/ocl/test/test_arithm.cpp index 43afd13424..9b20dbf89c 100644 --- a/modules/ocl/test/test_arithm.cpp +++ b/modules/ocl/test/test_arithm.cpp @@ -50,10 +50,6 @@ // //M*/ -//#define PRINT_CPU_TIME 1000 -//#define PRINT_TIME - - #include "test_precomp.hpp" #include @@ -65,392 +61,506 @@ using namespace cvtest; using namespace testing; using namespace std; -PARAM_TEST_CASE(ArithmTestBase, MatType, bool) +//////////////////////////////// LUT ///////////////////////////////////////////////// + +PARAM_TEST_CASE(Lut, int, int, bool, bool) { - int type; - cv::Scalar val; + int lut_depth; + int cn; + bool use_roi, same_cn; - //src mat - cv::Mat mat1; - cv::Mat mat2; - cv::Mat mask; + // src mat + cv::Mat src; + cv::Mat lut; cv::Mat dst; - cv::Mat dst1; //bak, for two outputs - // set up roi - int roicols; - int roirows; - int src1x; - int src1y; - int src2x; - int src2y; - int dstx; - int dsty; - int maskx; - int masky; - - //src mat with roi - cv::Mat mat1_roi; - cv::Mat mat2_roi; - cv::Mat mask_roi; + // src mat with roi + cv::Mat src_roi; + cv::Mat lut_roi; cv::Mat dst_roi; - cv::Mat dst1_roi; //bak - //ocl dst mat for testing + // ocl dst mat for testing cv::ocl::oclMat gdst_whole; - cv::ocl::oclMat gdst1_whole; //bak - //ocl mat with roi - cv::ocl::oclMat gmat1; - cv::ocl::oclMat gmat2; + // ocl mat with roi + cv::ocl::oclMat gsrc; + cv::ocl::oclMat glut; cv::ocl::oclMat gdst; - cv::ocl::oclMat gdst1; //bak - cv::ocl::oclMat gmask; virtual void SetUp() { - type = GET_PARAM(0); + lut_depth = GET_PARAM(0); + cn = GET_PARAM(1); + same_cn = GET_PARAM(2); + use_roi = GET_PARAM(3); + + const int src_type = CV_MAKE_TYPE(CV_8U, cn); + const int lut_type = CV_MAKE_TYPE(lut_depth, same_cn ? cn : 1); + const int dst_type = CV_MAKE_TYPE(lut_depth, cn); cv::RNG &rng = TS::ptr()->get_rng(); - cv::Size size(MWIDTH, MHEIGHT); + src = randomMat(rng, randomSize(MIN_VALUE, MAX_VALUE), src_type, 0, 256, false); + lut = randomMat(rng, use_roi ? randomSize(260, 300) : Size(256, 1), lut_type, 5, 16, false); + dst = randomMat(rng, use_roi ? randomSize(MIN_VALUE, MAX_VALUE) : src.size(), dst_type, 5, 16, false); + } - mat1 = randomMat(rng, size, type, 5, 16, false); - //mat2 = randomMat(rng, size, type, 5, 16, false); - mat2 = randomMat(rng, size, type, 5, 16, false); - dst = randomMat(rng, size, type, 5, 16, false); - dst1 = randomMat(rng, size, type, 5, 16, false); - mask = randomMat(rng, size, CV_8UC1, 0, 2, false); + void random_roi() + { + // set up roi + int roicols, roirows; + int srcx, srcy; + int lutx, luty; + int dstx, dsty; - cv::threshold(mask, mask, 0.5, 255., CV_8UC1); + if (use_roi) + { + // randomize ROI + cv::RNG &rng = TS::ptr()->get_rng(); - val = cv::Scalar(rng.uniform(-10.0, 10.0), rng.uniform(-10.0, 10.0), rng.uniform(-10.0, 10.0), rng.uniform(-10.0, 10.0)); + roicols = rng.uniform(1, MIN_VALUE); + roirows = rng.uniform(1, MIN_VALUE); - } + srcx = rng.uniform(0, src.cols - roicols); + srcy = rng.uniform(0, src.rows - roirows); + lutx = rng.uniform(0, lut.cols - 256); + luty = rng.uniform(0, lut.rows - 1); - void random_roi() - { -#ifdef RANDOMROI - //randomize ROI - cv::RNG &rng = TS::ptr()->get_rng(); - roicols = rng.uniform(1, mat1.cols); - roirows = rng.uniform(1, mat1.rows); - src1x = rng.uniform(0, mat1.cols - roicols); - src1y = rng.uniform(0, mat1.rows - roirows); - dstx = rng.uniform(0, dst.cols - roicols); - dsty = rng.uniform(0, dst.rows - roirows); - maskx = rng.uniform(0, mask.cols - roicols); - masky = rng.uniform(0, mask.rows - roirows); - src2x = rng.uniform(0, mat2.cols - roicols); - src2y = rng.uniform(0, mat2.rows - roirows); -#else - roicols = mat1.cols; - roirows = mat1.rows; - src1x = 0; - src1y = 0; - dstx = 0; - dsty = 0; - maskx = 0; - masky = 0; - src2x = 0; - src2y = 0; -#endif - mat1_roi = mat1(Rect(src1x, src1y, roicols, roirows)); - mat2_roi = mat2(Rect(src2x, src2y, roicols, roirows)); - mask_roi = mask(Rect(maskx, masky, roicols, roirows)); - dst_roi = dst(Rect(dstx, dsty, roicols, roirows)); - dst1_roi = dst1(Rect(dstx, dsty, roicols, roirows)); + dstx = rng.uniform(0, dst.cols - roicols); + dsty = rng.uniform(0, dst.rows - roirows); + } + else + { + roicols = src.cols; + roirows = src.rows; + srcx = srcy = 0; + lutx = luty = 0; + dstx = dsty = 0; + } + + src_roi = src(Rect(srcx, srcy, roicols, roirows)); + lut_roi = lut(Rect(lutx, luty, 256, 1)); + dst_roi = dst(Rect(dstx, dsty, roicols, roirows)); gdst_whole = dst; gdst = gdst_whole(Rect(dstx, dsty, roicols, roirows)); - gdst1_whole = dst1; - gdst1 = gdst1_whole(Rect(dstx, dsty, roicols, roirows)); - - gmat1 = mat1_roi; - gmat2 = mat2_roi; - gmask = mask_roi; //end + gsrc = src_roi; + glut = lut_roi; } void Near(double threshold = 0.) { EXPECT_MAT_NEAR(dst, Mat(gdst_whole), threshold); + EXPECT_MAT_NEAR(dst_roi, Mat(gdst), threshold); } +}; - void Near1(double threshold = 0.) +TEST_P(Lut, Mat) +{ + for (int j = 0; j < LOOP_TIMES; j++) { - EXPECT_MAT_NEAR(dst1, Mat(gdst1_whole), threshold); - } + random_roi(); -}; -////////////////////////////////lut///////////////////////////////////////////////// -struct Lut : ArithmTestBase {}; -#define VARNAME(A) string(#A); + cv::LUT(src_roi, lut_roi, dst_roi); + cv::ocl::LUT(gsrc, glut, gdst); + + Near(); + } +} +///////////////////////// ArithmTestBase /////////////////////////// -TEST_P(Lut, Mat) +PARAM_TEST_CASE(ArithmTestBase, int, int, bool) { + int depth; + int cn; + bool use_roi; + cv::Scalar val; - cv::Mat mat2(3, 512, CV_8UC1); - cv::RNG &rng = TS::ptr()->get_rng(); - rng.fill(mat2, cv::RNG::UNIFORM, cv::Scalar::all(0), cv::Scalar::all(256)); + // src mat + cv::Mat src1; + cv::Mat src2; + cv::Mat mask; + cv::Mat dst1; + cv::Mat dst2; // for two outputs + + // set up roi + int roicols, roirows; + int src1x, src1y; + int src2x, src2y; + int dst1x, dst1y; + int dst2x, dst2y; + int maskx, masky; + + // src mat with roi + cv::Mat src1_roi; + cv::Mat src2_roi; + cv::Mat mask_roi; + cv::Mat dst1_roi; + cv::Mat dst2_roi; // for two outputs + + // ocl dst mat for testing + cv::ocl::oclMat gdst1_whole; + cv::ocl::oclMat gdst2_whole; // for two outputs + + // ocl mat with roi + cv::ocl::oclMat gsrc1; + cv::ocl::oclMat gsrc2; + cv::ocl::oclMat gdst1; + cv::ocl::oclMat gdst2; // for two outputs + cv::ocl::oclMat gmask; - for(int j = 0; j < LOOP_TIMES; j ++) + virtual void SetUp() { - random_roi(); + depth = GET_PARAM(0); + cn = GET_PARAM(1); + use_roi = GET_PARAM(2); + const int type = CV_MAKE_TYPE(depth, cn); - src2x = rng.uniform( 0, mat2.cols - 256); - src2y = rng.uniform (0, mat2.rows - 1); + cv::RNG &rng = TS::ptr()->get_rng(); - cv::Mat mat2_roi = mat2(Rect(src2x, src2y, 256, 1)); + src1 = randomMat(rng, randomSize(MIN_VALUE, MAX_VALUE), type, 5, 16, false); + src2 = randomMat(rng, !use_roi ? src1.size() : randomSize(MIN_VALUE, MAX_VALUE), type, -15440, 14450, false); + dst1 = randomMat(rng, !use_roi ? src1.size() : randomSize(MIN_VALUE, MAX_VALUE), type, 5, 16, false); + dst2 = randomMat(rng, !use_roi ? src1.size() : randomSize(MIN_VALUE, MAX_VALUE), type, 5, 16, false); + mask = randomMat(rng, !use_roi ? src1.size() : randomSize(MIN_VALUE, MAX_VALUE), CV_8UC1, 0, 2, false); - cv::ocl::oclMat gmat2(mat2_roi); + cv::threshold(mask, mask, 0.5, 255., CV_8UC1); - cv::LUT(mat1_roi, mat2_roi, dst_roi); - cv::ocl::LUT(gmat1, gmat2, gdst); - Near(0); + val = cv::Scalar(rng.uniform(-100.0, 100.0), rng.uniform(-100.0, 100.0), + rng.uniform(-100.0, 100.0), rng.uniform(-100.0, 100.0)); + } + + void random_roi() + { + if (use_roi) + { + // randomize ROI + cv::RNG &rng = TS::ptr()->get_rng(); + + roicols = rng.uniform(1, MIN_VALUE); + roirows = rng.uniform(1, MIN_VALUE); + + src1x = rng.uniform(0, src1.cols - roicols); + src1y = rng.uniform(0, src1.rows - roirows); + src2x = rng.uniform(0, src2.cols - roicols); + src2y = rng.uniform(0, src2.rows - roirows); + + dst1x = rng.uniform(0, dst1.cols - roicols); + dst1y = rng.uniform(0, dst1.rows - roirows); + dst2x = rng.uniform(0, dst2.cols - roicols); + dst2y = rng.uniform(0, dst2.rows - roirows); + + maskx = rng.uniform(0, mask.cols - roicols); + masky = rng.uniform(0, mask.rows - roirows); + } + else + { + roicols = src1.cols; + roirows = src1.rows; + src1x = src1y = 0; + src2x = src2y = 0; + dst1x = dst1y = 0; + dst2x = dst2y = 0; + maskx = masky = 0; + } + + src1_roi = src1(Rect(src1x, src1y, roicols, roirows)); + src2_roi = src2(Rect(src2x, src2y, roicols, roirows)); + mask_roi = mask(Rect(maskx, masky, roicols, roirows)); + dst1_roi = dst1(Rect(dst1x, dst1y, roicols, roirows)); + dst2_roi = dst2(Rect(dst2x, dst2y, roicols, roirows)); + + gdst1_whole = dst1; + gdst1 = gdst1_whole(Rect(dst1x, dst1y, roicols, roirows)); + + gdst2_whole = dst2; + gdst2 = gdst2_whole(Rect(dst2x, dst2y, roicols, roirows)); + + gsrc1 = src1_roi; + gsrc2 = src2_roi; + gmask = mask_roi; + } + + void Near(double threshold = 0.) + { + EXPECT_MAT_NEAR(dst1, Mat(gdst1_whole), threshold); + EXPECT_MAT_NEAR(dst1_roi, Mat(gdst1), threshold); } -} + void Near1(double threshold = 0.) + { + EXPECT_MAT_NEAR(dst2, Mat(gdst2_whole), threshold); + EXPECT_MAT_NEAR(dst2_roi, Mat(gdst2), threshold); + } +}; -////////////////////////////////exp///////////////////////////////////////////////// -struct Exp : ArithmTestBase {}; +//////////////////////////////// Exp ///////////////////////////////////////////////// + +typedef ArithmTestBase Exp; TEST_P(Exp, Mat) { - for(int j = 0; j < LOOP_TIMES; j++) + for (int j = 0; j < LOOP_TIMES; j++) { random_roi(); - cv::exp(mat1_roi, dst_roi); - cv::ocl::exp(gmat1, gdst); + cv::exp(src1_roi, dst1_roi); + cv::ocl::exp(gsrc1, gdst1); + Near(2); } } +//////////////////////////////// Log ///////////////////////////////////////////////// -////////////////////////////////log///////////////////////////////////////////////// -struct Log : ArithmTestBase {}; +typedef ArithmTestBase Log; TEST_P(Log, Mat) { - for(int j = 0; j < LOOP_TIMES; j++) + for (int j = 0; j < LOOP_TIMES; j++) { random_roi(); - cv::log(mat1_roi, dst_roi); - cv::ocl::log(gmat1, gdst); + cv::log(src1_roi, dst1_roi); + cv::ocl::log(gsrc1, gdst1); Near(1); } } +//////////////////////////////// Add ///////////////////////////////////////////////// -////////////////////////////////add///////////////////////////////////////////////// -struct Add : ArithmTestBase {}; +typedef ArithmTestBase Add; TEST_P(Add, Mat) { - for(int j = 0; j < LOOP_TIMES; j++) + for (int j = 0; j < LOOP_TIMES; j++) { random_roi(); - cv::add(mat1_roi, mat2_roi, dst_roi); - cv::ocl::add(gmat1, gmat2, gdst); + cv::add(src1_roi, src2_roi, dst1_roi); + cv::ocl::add(gsrc1, gsrc2, gdst1); Near(0); } } TEST_P(Add, Mat_Mask) { - for(int j = 0; j < LOOP_TIMES; j++) + for (int j = 0; j < LOOP_TIMES; j++) { random_roi(); - cv::add(mat1_roi, mat2_roi, dst_roi, mask_roi); - cv::ocl::add(gmat1, gmat2, gdst, gmask); + cv::add(src1_roi, src2_roi, dst1_roi, mask_roi); + cv::ocl::add(gsrc1, gsrc2, gdst1, gmask); Near(0); } } TEST_P(Add, Scalar) { - for(int j = 0; j < LOOP_TIMES; j++) + for (int j = 0; j < LOOP_TIMES; j++) { random_roi(); - cv::add(mat1_roi, val, dst_roi); - cv::ocl::add(gmat1, val, gdst); + cv::add(src1_roi, val, dst1_roi); + cv::ocl::add(gsrc1, val, gdst1); Near(1e-5); } } TEST_P(Add, Scalar_Mask) { - for(int j = 0; j < LOOP_TIMES; j++) + for (int j = 0; j < LOOP_TIMES; j++) { random_roi(); - cv::add(mat1_roi, val, dst_roi, mask_roi); - cv::ocl::add(gmat1, val, gdst, gmask); + cv::add(src1_roi, val, dst1_roi, mask_roi); + cv::ocl::add(gsrc1, val, gdst1, gmask); Near(1e-5); } } +//////////////////////////////// Sub ///////////////////////////////////////////////// - -////////////////////////////////sub///////////////////////////////////////////////// -struct Sub : ArithmTestBase {}; +typedef ArithmTestBase Sub; TEST_P(Sub, Mat) { - for(int j = 0; j < LOOP_TIMES; j++) + for (int j = 0; j < LOOP_TIMES; j++) { random_roi(); - cv::subtract(mat1_roi, mat2_roi, dst_roi); - cv::ocl::subtract(gmat1, gmat2, gdst); + cv::subtract(src1_roi, src2_roi, dst1_roi); + cv::ocl::subtract(gsrc1, gsrc2, gdst1); + Near(0); } } TEST_P(Sub, Mat_Mask) { - for(int j = 0; j < LOOP_TIMES; j++) + for (int j = 0; j < LOOP_TIMES; j++) { random_roi(); - cv::subtract(mat1_roi, mat2_roi, dst_roi, mask_roi); - cv::ocl::subtract(gmat1, gmat2, gdst, gmask); + cv::subtract(src1_roi, src2_roi, dst1_roi, mask_roi); + cv::ocl::subtract(gsrc1, gsrc2, gdst1, gmask); Near(0); } } TEST_P(Sub, Scalar) { - for(int j = 0; j < LOOP_TIMES; j++) + for (int j = 0; j < LOOP_TIMES; j++) { random_roi(); - cv::subtract(mat1_roi, val, dst_roi); - cv::ocl::subtract(gmat1, val, gdst); + cv::subtract(src1_roi, val, dst1_roi); + cv::ocl::subtract(gsrc1, val, gdst1); + Near(1e-5); } } TEST_P(Sub, Scalar_Mask) { - for(int j = 0; j < LOOP_TIMES; j++) + for (int j = 0; j < LOOP_TIMES; j++) { random_roi(); - cv::subtract(mat1_roi, val, dst_roi, mask_roi); - cv::ocl::subtract(gmat1, val, gdst, gmask); + cv::subtract(src1_roi, val, dst1_roi, mask_roi); + cv::ocl::subtract(gsrc1, val, gdst1, gmask); Near(1e-5); } } +//////////////////////////////// Mul ///////////////////////////////////////////////// - -////////////////////////////////Mul///////////////////////////////////////////////// -struct Mul : ArithmTestBase {}; +typedef ArithmTestBase Mul; TEST_P(Mul, Mat) { - for(int j = 0; j < LOOP_TIMES; j++) + for (int j = 0; j < LOOP_TIMES; j++) { random_roi(); - cv::multiply(mat1_roi, mat2_roi, dst_roi); - cv::ocl::multiply(gmat1, gmat2, gdst); + cv::multiply(src1_roi, src2_roi, dst1_roi); + cv::ocl::multiply(gsrc1, gsrc2, gdst1); Near(0); } } -TEST_P(Mul, Mat_Scalar) +TEST_P(Mul, Scalar) { - for(int j = 0; j < LOOP_TIMES; j++) + for (int j = 0; j < LOOP_TIMES; j++) { random_roi(); - cv::RNG &rng = TS::ptr()->get_rng(); - double s = rng.uniform(-10.0, 10.0); + cv::multiply(val[0], src1_roi, dst1_roi); + cv::ocl::multiply(val[0], gsrc1, gdst1); - cv::multiply(mat1_roi, mat2_roi, dst_roi, s); - cv::ocl::multiply(gmat1, gmat2, gdst, s); - Near(.001); + Near(gdst1.depth() >= CV_32F ? 1e-3 : 1); } } +TEST_P(Mul, Mat_Scalar) +{ + for (int j = 0; j < LOOP_TIMES; j++) + { + random_roi(); + + cv::multiply(src1_roi, src2_roi, dst1_roi, val[0]); + cv::ocl::multiply(gsrc1, gsrc2, gdst1, val[0]); + + Near(gdst1.depth() >= CV_32F ? 1e-3 : 1); + } +} + +//////////////////////////////// Div ///////////////////////////////////////////////// -struct Div : ArithmTestBase {}; +typedef ArithmTestBase Div; TEST_P(Div, Mat) { - for(int j = 0; j < LOOP_TIMES; j++) + for (int j = 0; j < LOOP_TIMES; j++) { random_roi(); - cv::divide(mat1_roi, mat2_roi, dst_roi); - cv::ocl::divide(gmat1, gmat2, gdst); + cv::divide(src1_roi, src2_roi, dst1_roi); + cv::ocl::divide(gsrc1, gsrc2, gdst1); Near(1); } } +TEST_P(Div, Scalar) +{ + for (int j = 0; j < LOOP_TIMES; j++) + { + random_roi(); + + cv::divide(val[0], src1_roi, dst1_roi); + cv::ocl::divide(val[0], gsrc1, gdst1); + + Near(gdst1.depth() >= CV_32F ? 1e-3 : 1); + } +} + + TEST_P(Div, Mat_Scalar) { - for(int j = 0; j < LOOP_TIMES; j++) + for (int j = 0; j < LOOP_TIMES; j++) { random_roi(); - cv::RNG &rng = TS::ptr()->get_rng(); - double s = rng.uniform(-10.0, 10.0); + cv::divide(src1_roi, src2_roi, dst1_roi, val[0]); + cv::ocl::divide(gsrc1, gsrc2, gdst1, val[0]); - cv::divide(mat1_roi, mat2_roi, dst_roi, s); - cv::ocl::divide(gmat1, gmat2, gdst, s); - Near(.001); + Near(gdst1.depth() >= CV_32F ? 1e-3 : 1); } } +//////////////////////////////// Absdiff ///////////////////////////////////////////////// -struct Absdiff : ArithmTestBase {}; +typedef ArithmTestBase Absdiff; TEST_P(Absdiff, Mat) { - for(int j = 0; j < LOOP_TIMES; j++) + for (int j = 0; j < LOOP_TIMES; j++) { random_roi(); - cv::absdiff(mat1_roi, mat2_roi, dst_roi); - cv::ocl::absdiff(gmat1, gmat2, gdst); + cv::absdiff(src1_roi, src2_roi, dst1_roi); + cv::ocl::absdiff(gsrc1, gsrc2, gdst1); Near(0); } } TEST_P(Absdiff, Mat_Scalar) { - for(int j = 0; j < LOOP_TIMES; j++) + for (int j = 0; j < LOOP_TIMES; j++) { random_roi(); - cv::absdiff(mat1_roi, val, dst_roi); - cv::ocl::absdiff(gmat1, val, gdst); + cv::absdiff(src1_roi, val, dst1_roi); + cv::ocl::absdiff(gsrc1, val, gdst1); Near(1e-5); } } +//////////////////////////////// CartToPolar ///////////////////////////////////////////////// - -struct CartToPolar : ArithmTestBase {}; +typedef ArithmTestBase CartToPolar; TEST_P(CartToPolar, angleInDegree) { - for(int j = 0; j < LOOP_TIMES; j++) + for (int j = 0; j < LOOP_TIMES; j++) { random_roi(); - cv::cartToPolar(mat1_roi, mat2_roi, dst_roi, dst1_roi, 1); - cv::ocl::cartToPolar(gmat1, gmat2, gdst, gdst1, 1); + cv::cartToPolar(src1_roi, src2_roi, dst1_roi, dst2_roi, true); + cv::ocl::cartToPolar(gsrc1, gsrc2, gdst1, gdst2, true); Near(.5); Near1(.5); } @@ -458,28 +568,30 @@ TEST_P(CartToPolar, angleInDegree) TEST_P(CartToPolar, angleInRadians) { - for(int j = 0; j < LOOP_TIMES; j++) + for (int j = 0; j < LOOP_TIMES; j++) { random_roi(); - cv::cartToPolar(mat1_roi, mat2_roi, dst_roi, dst1_roi, 0); - cv::ocl::cartToPolar(gmat1, gmat2, gdst, gdst1, 0); + cv::cartToPolar(src1_roi, src2_roi, dst1_roi, dst2_roi); + cv::ocl::cartToPolar(gsrc1, gsrc2, gdst1, gdst2); Near(.5); Near1(.5); } } +//////////////////////////////// PolarToCart ///////////////////////////////////////////////// -struct PolarToCart : ArithmTestBase {}; +typedef ArithmTestBase PolarToCart; TEST_P(PolarToCart, angleInDegree) { - for(int j = 0; j < LOOP_TIMES; j++) + for (int j = 0; j < LOOP_TIMES; j++) { random_roi(); - cv::polarToCart(mat1_roi, mat2_roi, dst_roi, dst1_roi, 1); - cv::ocl::polarToCart(gmat1, gmat2, gdst, gdst1, 1); + cv::polarToCart(src1_roi, src2_roi, dst1_roi, dst2_roi, true); + cv::ocl::polarToCart(gsrc1, gsrc2, gdst1, gdst2, true); + Near(.5); Near1(.5); } @@ -487,144 +599,179 @@ TEST_P(PolarToCart, angleInDegree) TEST_P(PolarToCart, angleInRadians) { - for(int j = 0; j < LOOP_TIMES; j++) + for (int j = 0; j < LOOP_TIMES; j++) { random_roi(); - cv::polarToCart(mat1_roi, mat2_roi, dst_roi, dst1_roi, 0); - cv::ocl::polarToCart(gmat1, gmat2, gdst, gdst1, 0); + cv::polarToCart(src1_roi, src2_roi, dst1_roi, dst2_roi); + cv::ocl::polarToCart(gsrc1, gsrc2, gdst1, gdst2); + Near(.5); Near1(.5); } } +//////////////////////////////// Magnitude ///////////////////////////////////////////////// - - -struct Magnitude : ArithmTestBase {}; +typedef ArithmTestBase Magnitude; TEST_P(Magnitude, Mat) { - for(int j = 0; j < LOOP_TIMES; j++) + for (int j = 0; j < LOOP_TIMES; j++) { random_roi(); - cv::magnitude(mat1_roi, mat2_roi, dst_roi); - cv::ocl::magnitude(gmat1, gmat2, gdst); - Near(1e-5); + cv::magnitude(src1_roi, src2_roi, dst1_roi); + cv::ocl::magnitude(gsrc1, gsrc2, gdst1); + Near(depth == CV_64F ? 1e-5 : 1e-2); } } +//////////////////////////////// Transpose ///////////////////////////////////////////////// -struct Transpose : ArithmTestBase {}; +typedef ArithmTestBase Transpose; TEST_P(Transpose, Mat) { - for(int j = 0; j < LOOP_TIMES; j++) + for (int j = 0; j < LOOP_TIMES; j++) { random_roi(); - cv::transpose(mat1_roi, dst_roi); - cv::ocl::transpose(gmat1, gdst); + cv::transpose(src1_roi, dst1_roi); + cv::ocl::transpose(gsrc1, gdst1); + Near(1e-5); } } +TEST_P(Transpose, SquareInplace) +{ + cv::RNG &rng = TS::ptr()->get_rng(); + int value = randomInt(MIN_VALUE, MAX_VALUE); + src1 = randomMat(rng, Size(value, value), CV_MAKE_TYPE(depth, cn), 5, 16, false); + + if (use_roi) + { + roirows = roicols = randomInt(1, src1.cols); + + src1x = randomInt(0, src1.cols - roicols); + src1y = randomInt(0, src1.rows - roirows); + } + else + { + roicols = roirows = src1.cols; + src1x = src1y = 0; + } + + Rect r(src1x, src1y, roicols, roirows); + src1_roi = src1(r); + gdst1_whole = src1; + gdst1 = gdst1_whole(r); + + for (int j = 0; j < LOOP_TIMES; j++) + { + cv::transpose(src1_roi, src1_roi); + cv::ocl::transpose(gdst1, gdst1); + + EXPECT_MAT_NEAR(src1, Mat(gdst1_whole), 0.0); + EXPECT_MAT_NEAR(src1_roi, Mat(gdst1), 0.0); + } +} + +//////////////////////////////// Flip ///////////////////////////////////////////////// -struct Flip : ArithmTestBase {}; +typedef ArithmTestBase Flip; TEST_P(Flip, X) { - for(int j = 0; j < LOOP_TIMES; j++) + for (int j = 0; j < LOOP_TIMES; j++) { random_roi(); - cv::flip(mat1_roi, dst_roi, 0); - cv::ocl::flip(gmat1, gdst, 0); + cv::flip(src1_roi, dst1_roi, 0); + cv::ocl::flip(gsrc1, gdst1, 0); Near(1e-5); } } TEST_P(Flip, Y) { - for(int j = 0; j < LOOP_TIMES; j++) + for (int j = 0; j < LOOP_TIMES; j++) { random_roi(); - cv::flip(mat1_roi, dst_roi, 1); - cv::ocl::flip(gmat1, gdst, 1); + cv::flip(src1_roi, dst1_roi, 1); + cv::ocl::flip(gsrc1, gdst1, 1); Near(1e-5); } } TEST_P(Flip, BOTH) { - for(int j = 0; j < LOOP_TIMES; j++) + for (int j = 0; j < LOOP_TIMES; j++) { random_roi(); - cv::flip(mat1_roi, dst_roi, -1); - cv::ocl::flip(gmat1, gdst, -1); + cv::flip(src1_roi, dst1_roi, -1); + cv::ocl::flip(gsrc1, gdst1, -1); Near(1e-5); } } +//////////////////////////////// MinMax ///////////////////////////////////////////////// -struct MinMax : ArithmTestBase {}; +typedef ArithmTestBase MinMax; TEST_P(MinMax, MAT) { - for(int j = 0; j < LOOP_TIMES; j++) + for (int j = 0; j < LOOP_TIMES; j++) { random_roi(); + double minVal, maxVal; - cv::Point minLoc, maxLoc; - if (mat1.depth() != CV_8S) - { - cv::minMaxLoc(mat1_roi, &minVal, &maxVal, &minLoc, &maxLoc); - } + if (src1.depth() != CV_8S) + cv::minMaxIdx(src1_roi, &minVal, &maxVal, NULL, NULL); else { minVal = std::numeric_limits::max(); maxVal = -std::numeric_limits::max(); - for (int i = 0; i < mat1_roi.rows; ++i) - for (int j = 0; j < mat1_roi.cols; ++j) + for (int i = 0; i < src1_roi.rows; ++i) + for (int j = 0; j < src1_roi.cols; ++j) { - signed char val = mat1_roi.at(i, j); + signed char val = src1_roi.at(i, j); if (val < minVal) minVal = val; - if (val > maxVal) maxVal = val; + else if (val > maxVal) maxVal = val; } } double minVal_, maxVal_; - cv::ocl::minMax(gmat1, &minVal_, &maxVal_); + cv::ocl::minMax(gsrc1, &minVal_, &maxVal_); EXPECT_DOUBLE_EQ(minVal_, minVal); EXPECT_DOUBLE_EQ(maxVal_, maxVal); } } -TEST_P(MinMax, MASK) +TEST_P(MinMax, DISABLED_MASK) { - for(int j = 0; j < LOOP_TIMES; j++) + for (int j = 0; j < LOOP_TIMES; j++) { random_roi(); + double minVal, maxVal; cv::Point minLoc, maxLoc; - if (mat1.depth() != CV_8S) - { - cv::minMaxLoc(mat1_roi, &minVal, &maxVal, &minLoc, &maxLoc, mask_roi); - } + if (src1.depth() != CV_8S) + cv::minMaxLoc(src1_roi, &minVal, &maxVal, &minLoc, &maxLoc, mask_roi); else { minVal = std::numeric_limits::max(); maxVal = -std::numeric_limits::max(); - for (int i = 0; i < mat1_roi.rows; ++i) - for (int j = 0; j < mat1_roi.cols; ++j) + for (int i = 0; i < src1_roi.rows; ++i) + for (int j = 0; j < src1_roi.cols; ++j) { - signed char val = mat1_roi.at(i, j); + signed char val = src1_roi.at(i, j); unsigned char m = mask_roi.at(i, j); if (val < minVal && m) minVal = val; if (val > maxVal && m) maxVal = val; @@ -632,36 +779,37 @@ TEST_P(MinMax, MASK) } double minVal_, maxVal_; - cv::ocl::minMax(gmat1, &minVal_, &maxVal_, gmask); + cv::ocl::minMax(gsrc1, &minVal_, &maxVal_, gmask); - EXPECT_DOUBLE_EQ(minVal_, minVal); - EXPECT_DOUBLE_EQ(maxVal_, maxVal); + EXPECT_DOUBLE_EQ(minVal, minVal_); + EXPECT_DOUBLE_EQ(maxVal, maxVal_); } } +//////////////////////////////// MinMaxLoc ///////////////////////////////////////////////// -struct MinMaxLoc : ArithmTestBase {}; +typedef ArithmTestBase MinMaxLoc; TEST_P(MinMaxLoc, MAT) { - for(int j = 0; j < LOOP_TIMES; j++) + for (int j = 0; j < LOOP_TIMES; j++) { random_roi(); + double minVal, maxVal; cv::Point minLoc, maxLoc; - int depth = mat1.depth(); + int depth = src1.depth(); + if (depth != CV_8S) - { - cv::minMaxLoc(mat1_roi, &minVal, &maxVal, &minLoc, &maxLoc); - } + cv::minMaxLoc(src1_roi, &minVal, &maxVal, &minLoc, &maxLoc); else { minVal = std::numeric_limits::max(); maxVal = -std::numeric_limits::max(); - for (int i = 0; i < mat1_roi.rows; ++i) - for (int j = 0; j < mat1_roi.cols; ++j) + for (int i = 0; i < src1_roi.rows; ++i) + for (int j = 0; j < src1_roi.cols; ++j) { - signed char val = mat1_roi.at(i, j); + signed char val = src1_roi.at(i, j); if (val < minVal) { minVal = val; @@ -679,71 +827,71 @@ TEST_P(MinMaxLoc, MAT) double minVal_, maxVal_; cv::Point minLoc_, maxLoc_; - cv::ocl::minMaxLoc(gmat1, &minVal_, &maxVal_, &minLoc_, &maxLoc_, cv::ocl::oclMat()); + cv::ocl::minMaxLoc(gsrc1, &minVal_, &maxVal_, &minLoc_, &maxLoc_, cv::ocl::oclMat()); double error0 = 0., error1 = 0., minlocVal = 0., minlocVal_ = 0., maxlocVal = 0., maxlocVal_ = 0.; - if(depth == 0) + if (depth == 0) { - minlocVal = mat1_roi.at(minLoc); - minlocVal_ = mat1_roi.at(minLoc_); - maxlocVal = mat1_roi.at(maxLoc); - maxlocVal_ = mat1_roi.at(maxLoc_); - error0 = ::abs(mat1_roi.at(minLoc_) - mat1_roi.at(minLoc)); - error1 = ::abs(mat1_roi.at(maxLoc_) - mat1_roi.at(maxLoc)); + minlocVal = src1_roi.at(minLoc); + minlocVal_ = src1_roi.at(minLoc_); + maxlocVal = src1_roi.at(maxLoc); + maxlocVal_ = src1_roi.at(maxLoc_); + error0 = ::abs(src1_roi.at(minLoc_) - src1_roi.at(minLoc)); + error1 = ::abs(src1_roi.at(maxLoc_) - src1_roi.at(maxLoc)); } - if(depth == 1) + if (depth == 1) { - minlocVal = mat1_roi.at(minLoc); - minlocVal_ = mat1_roi.at(minLoc_); - maxlocVal = mat1_roi.at(maxLoc); - maxlocVal_ = mat1_roi.at(maxLoc_); - error0 = ::abs(mat1_roi.at(minLoc_) - mat1_roi.at(minLoc)); - error1 = ::abs(mat1_roi.at(maxLoc_) - mat1_roi.at(maxLoc)); + minlocVal = src1_roi.at(minLoc); + minlocVal_ = src1_roi.at(minLoc_); + maxlocVal = src1_roi.at(maxLoc); + maxlocVal_ = src1_roi.at(maxLoc_); + error0 = ::abs(src1_roi.at(minLoc_) - src1_roi.at(minLoc)); + error1 = ::abs(src1_roi.at(maxLoc_) - src1_roi.at(maxLoc)); } - if(depth == 2) + if (depth == 2) { - minlocVal = mat1_roi.at(minLoc); - minlocVal_ = mat1_roi.at(minLoc_); - maxlocVal = mat1_roi.at(maxLoc); - maxlocVal_ = mat1_roi.at(maxLoc_); - error0 = ::abs(mat1_roi.at(minLoc_) - mat1_roi.at(minLoc)); - error1 = ::abs(mat1_roi.at(maxLoc_) - mat1_roi.at(maxLoc)); + minlocVal = src1_roi.at(minLoc); + minlocVal_ = src1_roi.at(minLoc_); + maxlocVal = src1_roi.at(maxLoc); + maxlocVal_ = src1_roi.at(maxLoc_); + error0 = ::abs(src1_roi.at(minLoc_) - src1_roi.at(minLoc)); + error1 = ::abs(src1_roi.at(maxLoc_) - src1_roi.at(maxLoc)); } - if(depth == 3) + if (depth == 3) { - minlocVal = mat1_roi.at(minLoc); - minlocVal_ = mat1_roi.at(minLoc_); - maxlocVal = mat1_roi.at(maxLoc); - maxlocVal_ = mat1_roi.at(maxLoc_); - error0 = ::abs(mat1_roi.at(minLoc_) - mat1_roi.at(minLoc)); - error1 = ::abs(mat1_roi.at(maxLoc_) - mat1_roi.at(maxLoc)); + minlocVal = src1_roi.at(minLoc); + minlocVal_ = src1_roi.at(minLoc_); + maxlocVal = src1_roi.at(maxLoc); + maxlocVal_ = src1_roi.at(maxLoc_); + error0 = ::abs(src1_roi.at(minLoc_) - src1_roi.at(minLoc)); + error1 = ::abs(src1_roi.at(maxLoc_) - src1_roi.at(maxLoc)); } - if(depth == 4) + if (depth == 4) { - minlocVal = mat1_roi.at(minLoc); - minlocVal_ = mat1_roi.at(minLoc_); - maxlocVal = mat1_roi.at(maxLoc); - maxlocVal_ = mat1_roi.at(maxLoc_); - error0 = ::abs(mat1_roi.at(minLoc_) - mat1_roi.at(minLoc)); - error1 = ::abs(mat1_roi.at(maxLoc_) - mat1_roi.at(maxLoc)); + minlocVal = src1_roi.at(minLoc); + minlocVal_ = src1_roi.at(minLoc_); + maxlocVal = src1_roi.at(maxLoc); + maxlocVal_ = src1_roi.at(maxLoc_); + error0 = ::abs(src1_roi.at(minLoc_) - src1_roi.at(minLoc)); + error1 = ::abs(src1_roi.at(maxLoc_) - src1_roi.at(maxLoc)); } - if(depth == 5) + if (depth == 5) { - minlocVal = mat1_roi.at(minLoc); - minlocVal_ = mat1_roi.at(minLoc_); - maxlocVal = mat1_roi.at(maxLoc); - maxlocVal_ = mat1_roi.at(maxLoc_); - error0 = ::abs(mat1_roi.at(minLoc_) - mat1_roi.at(minLoc)); - error1 = ::abs(mat1_roi.at(maxLoc_) - mat1_roi.at(maxLoc)); + minlocVal = src1_roi.at(minLoc); + minlocVal_ = src1_roi.at(minLoc_); + maxlocVal = src1_roi.at(maxLoc); + maxlocVal_ = src1_roi.at(maxLoc_); + error0 = ::abs(src1_roi.at(minLoc_) - src1_roi.at(minLoc)); + error1 = ::abs(src1_roi.at(maxLoc_) - src1_roi.at(maxLoc)); } - if(depth == 6) + if (depth == 6) { - minlocVal = mat1_roi.at(minLoc); - minlocVal_ = mat1_roi.at(minLoc_); - maxlocVal = mat1_roi.at(maxLoc); - maxlocVal_ = mat1_roi.at(maxLoc_); - error0 = ::abs(mat1_roi.at(minLoc_) - mat1_roi.at(minLoc)); - error1 = ::abs(mat1_roi.at(maxLoc_) - mat1_roi.at(maxLoc)); + minlocVal = src1_roi.at(minLoc); + minlocVal_ = src1_roi.at(minLoc_); + maxlocVal = src1_roi.at(maxLoc); + maxlocVal_ = src1_roi.at(maxLoc_); + error0 = ::abs(src1_roi.at(minLoc_) - src1_roi.at(minLoc)); + error1 = ::abs(src1_roi.at(maxLoc_) - src1_roi.at(maxLoc)); } EXPECT_DOUBLE_EQ(minVal_, minVal); @@ -756,27 +904,24 @@ TEST_P(MinMaxLoc, MAT) } } - TEST_P(MinMaxLoc, MASK) { - for(int j = 0; j < LOOP_TIMES; j++) + for (int j = 0; j < LOOP_TIMES; j++) { random_roi(); double minVal, maxVal; cv::Point minLoc, maxLoc; - int depth = mat1.depth(); + int depth = src1.depth(); if (depth != CV_8S) - { - cv::minMaxLoc(mat1_roi, &minVal, &maxVal, &minLoc, &maxLoc, mask_roi); - } + cv::minMaxLoc(src1_roi, &minVal, &maxVal, &minLoc, &maxLoc, mask_roi); else { minVal = std::numeric_limits::max(); maxVal = -std::numeric_limits::max(); - for (int i = 0; i < mat1_roi.rows; ++i) - for (int j = 0; j < mat1_roi.cols; ++j) + for (int i = 0; i < src1_roi.rows; ++i) + for (int j = 0; j < src1_roi.cols; ++j) { - signed char val = mat1_roi.at(i, j); + signed char val = src1_roi.at(i, j); unsigned char m = mask_roi.at(i , j); if (val < minVal && m) { @@ -795,72 +940,72 @@ TEST_P(MinMaxLoc, MASK) double minVal_, maxVal_; cv::Point minLoc_, maxLoc_; - cv::ocl::minMaxLoc(gmat1, &minVal_, &maxVal_, &minLoc_, &maxLoc_, gmask); + cv::ocl::minMaxLoc(gsrc1, &minVal_, &maxVal_, &minLoc_, &maxLoc_, gmask); double error0 = 0., error1 = 0., minlocVal = 0., minlocVal_ = 0., maxlocVal = 0., maxlocVal_ = 0.; - if(minLoc_.x == -1 || minLoc_.y == -1 || maxLoc_.x == -1 || maxLoc_.y == -1) continue; - if(depth == 0) + if (minLoc_.x == -1 || minLoc_.y == -1 || maxLoc_.x == -1 || maxLoc_.y == -1) continue; + if (depth == 0) { - minlocVal = mat1_roi.at(minLoc); - minlocVal_ = mat1_roi.at(minLoc_); - maxlocVal = mat1_roi.at(maxLoc); - maxlocVal_ = mat1_roi.at(maxLoc_); - error0 = ::abs(mat1_roi.at(minLoc_) - mat1_roi.at(minLoc)); - error1 = ::abs(mat1_roi.at(maxLoc_) - mat1_roi.at(maxLoc)); + minlocVal = src1_roi.at(minLoc); + minlocVal_ = src1_roi.at(minLoc_); + maxlocVal = src1_roi.at(maxLoc); + maxlocVal_ = src1_roi.at(maxLoc_); + error0 = ::abs(src1_roi.at(minLoc_) - src1_roi.at(minLoc)); + error1 = ::abs(src1_roi.at(maxLoc_) - src1_roi.at(maxLoc)); } - if(depth == 1) + if (depth == 1) { - minlocVal = mat1_roi.at(minLoc); - minlocVal_ = mat1_roi.at(minLoc_); - maxlocVal = mat1_roi.at(maxLoc); - maxlocVal_ = mat1_roi.at(maxLoc_); - error0 = ::abs(mat1_roi.at(minLoc_) - mat1_roi.at(minLoc)); - error1 = ::abs(mat1_roi.at(maxLoc_) - mat1_roi.at(maxLoc)); + minlocVal = src1_roi.at(minLoc); + minlocVal_ = src1_roi.at(minLoc_); + maxlocVal = src1_roi.at(maxLoc); + maxlocVal_ = src1_roi.at(maxLoc_); + error0 = ::abs(src1_roi.at(minLoc_) - src1_roi.at(minLoc)); + error1 = ::abs(src1_roi.at(maxLoc_) - src1_roi.at(maxLoc)); } - if(depth == 2) + if (depth == 2) { - minlocVal = mat1_roi.at(minLoc); - minlocVal_ = mat1_roi.at(minLoc_); - maxlocVal = mat1_roi.at(maxLoc); - maxlocVal_ = mat1_roi.at(maxLoc_); - error0 = ::abs(mat1_roi.at(minLoc_) - mat1_roi.at(minLoc)); - error1 = ::abs(mat1_roi.at(maxLoc_) - mat1_roi.at(maxLoc)); + minlocVal = src1_roi.at(minLoc); + minlocVal_ = src1_roi.at(minLoc_); + maxlocVal = src1_roi.at(maxLoc); + maxlocVal_ = src1_roi.at(maxLoc_); + error0 = ::abs(src1_roi.at(minLoc_) - src1_roi.at(minLoc)); + error1 = ::abs(src1_roi.at(maxLoc_) - src1_roi.at(maxLoc)); } - if(depth == 3) + if (depth == 3) { - minlocVal = mat1_roi.at(minLoc); - minlocVal_ = mat1_roi.at(minLoc_); - maxlocVal = mat1_roi.at(maxLoc); - maxlocVal_ = mat1_roi.at(maxLoc_); - error0 = ::abs(mat1_roi.at(minLoc_) - mat1_roi.at(minLoc)); - error1 = ::abs(mat1_roi.at(maxLoc_) - mat1_roi.at(maxLoc)); + minlocVal = src1_roi.at(minLoc); + minlocVal_ = src1_roi.at(minLoc_); + maxlocVal = src1_roi.at(maxLoc); + maxlocVal_ = src1_roi.at(maxLoc_); + error0 = ::abs(src1_roi.at(minLoc_) - src1_roi.at(minLoc)); + error1 = ::abs(src1_roi.at(maxLoc_) - src1_roi.at(maxLoc)); } - if(depth == 4) + if (depth == 4) { - minlocVal = mat1_roi.at(minLoc); - minlocVal_ = mat1_roi.at(minLoc_); - maxlocVal = mat1_roi.at(maxLoc); - maxlocVal_ = mat1_roi.at(maxLoc_); - error0 = ::abs(mat1_roi.at(minLoc_) - mat1_roi.at(minLoc)); - error1 = ::abs(mat1_roi.at(maxLoc_) - mat1_roi.at(maxLoc)); + minlocVal = src1_roi.at(minLoc); + minlocVal_ = src1_roi.at(minLoc_); + maxlocVal = src1_roi.at(maxLoc); + maxlocVal_ = src1_roi.at(maxLoc_); + error0 = ::abs(src1_roi.at(minLoc_) - src1_roi.at(minLoc)); + error1 = ::abs(src1_roi.at(maxLoc_) - src1_roi.at(maxLoc)); } - if(depth == 5) + if (depth == 5) { - minlocVal = mat1_roi.at(minLoc); - minlocVal_ = mat1_roi.at(minLoc_); - maxlocVal = mat1_roi.at(maxLoc); - maxlocVal_ = mat1_roi.at(maxLoc_); - error0 = ::abs(mat1_roi.at(minLoc_) - mat1_roi.at(minLoc)); - error1 = ::abs(mat1_roi.at(maxLoc_) - mat1_roi.at(maxLoc)); + minlocVal = src1_roi.at(minLoc); + minlocVal_ = src1_roi.at(minLoc_); + maxlocVal = src1_roi.at(maxLoc); + maxlocVal_ = src1_roi.at(maxLoc_); + error0 = ::abs(src1_roi.at(minLoc_) - src1_roi.at(minLoc)); + error1 = ::abs(src1_roi.at(maxLoc_) - src1_roi.at(maxLoc)); } - if(depth == 6) + if (depth == 6) { - minlocVal = mat1_roi.at(minLoc); - minlocVal_ = mat1_roi.at(minLoc_); - maxlocVal = mat1_roi.at(maxLoc); - maxlocVal_ = mat1_roi.at(maxLoc_); - error0 = ::abs(mat1_roi.at(minLoc_) - mat1_roi.at(minLoc)); - error1 = ::abs(mat1_roi.at(maxLoc_) - mat1_roi.at(maxLoc)); + minlocVal = src1_roi.at(minLoc); + minlocVal_ = src1_roi.at(minLoc_); + maxlocVal = src1_roi.at(maxLoc); + maxlocVal_ = src1_roi.at(maxLoc_); + error0 = ::abs(src1_roi.at(minLoc_) - src1_roi.at(minLoc)); + error1 = ::abs(src1_roi.at(maxLoc_) - src1_roi.at(maxLoc)); } EXPECT_DOUBLE_EQ(minVal_, minVal); @@ -873,16 +1018,18 @@ TEST_P(MinMaxLoc, MASK) } } +//////////////////////////////// Sum ///////////////////////////////////////////////// -struct Sum : ArithmTestBase {}; +typedef ArithmTestBase Sum; -TEST_P(Sum, MAT) +TEST_P(Sum, DISABLED_MAT) { - for(int j = 0; j < LOOP_TIMES; j++) + for (int j = 0; j < LOOP_TIMES; j++) { random_roi(); - Scalar cpures = cv::sum(mat1_roi); - Scalar gpures = cv::ocl::sum(gmat1); + + Scalar cpures = cv::sum(src1_roi); + Scalar gpures = cv::ocl::sum(gsrc1); //check results EXPECT_NEAR(cpures[0], gpures[0], 0.1); @@ -892,398 +1039,294 @@ TEST_P(Sum, MAT) } } +//////////////////////////////// CountNonZero ///////////////////////////////////////////////// -struct CountNonZero : ArithmTestBase {}; +typedef ArithmTestBase CountNonZero; TEST_P(CountNonZero, MAT) { - for(int j = 0; j < LOOP_TIMES; j++) + for (int j = 0; j < LOOP_TIMES; j++) { random_roi(); - int cpures = cv::countNonZero(mat1_roi); - int gpures = cv::ocl::countNonZero(gmat1); + int cpures = cv::countNonZero(src1_roi); + int gpures = cv::ocl::countNonZero(gsrc1); EXPECT_DOUBLE_EQ((double)cpures, (double)gpures); } } +//////////////////////////////// Phase ///////////////////////////////////////////////// +typedef ArithmTestBase Phase; -////////////////////////////////phase///////////////////////////////////////////////// -struct Phase : ArithmTestBase {}; - -TEST_P(Phase, Mat) +TEST_P(Phase, DISABLED_Mat) { - if(mat1.depth() != CV_32F && mat1.depth() != CV_64F) - { - cout << "\tUnsupported type\t\n"; - } - for(int angelInDegrees = 0; angelInDegrees < 2; angelInDegrees++) + for (int angelInDegrees = 0; angelInDegrees < 2; angelInDegrees++) { - for(int j = 0; j < LOOP_TIMES; j++) + for (int j = 0; j < LOOP_TIMES; j++) { random_roi(); - cv::phase(mat1_roi, mat2_roi, dst_roi, angelInDegrees ? true : false); - cv::ocl::phase(gmat1, gmat2, gdst, angelInDegrees ? true : false); + cv::phase(src1_roi, src2_roi, dst1_roi, angelInDegrees ? true : false); + cv::ocl::phase(gsrc1, gsrc2, gdst1, angelInDegrees ? true : false); Near(1e-2); } } } +//////////////////////////////// Bitwise_and ///////////////////////////////////////////////// -////////////////////////////////bitwise_and///////////////////////////////////////////////// -struct Bitwise_and : ArithmTestBase {}; +typedef ArithmTestBase Bitwise_and; TEST_P(Bitwise_and, Mat) { - for(int j = 0; j < LOOP_TIMES; j++) + for (int j = 0; j < LOOP_TIMES; j++) { random_roi(); - cv::bitwise_and(mat1_roi, mat2_roi, dst_roi); - cv::ocl::bitwise_and(gmat1, gmat2, gdst); + cv::bitwise_and(src1_roi, src2_roi, dst1_roi); + cv::ocl::bitwise_and(gsrc1, gsrc2, gdst1); Near(0); } } TEST_P(Bitwise_and, Mat_Mask) { - for(int j = 0; j < LOOP_TIMES; j++) + for (int j = 0; j < LOOP_TIMES; j++) { random_roi(); - cv::bitwise_and(mat1_roi, mat2_roi, dst_roi, mask_roi); - cv::ocl::bitwise_and(gmat1, gmat2, gdst, gmask); + cv::bitwise_and(src1_roi, src2_roi, dst1_roi, mask_roi); + cv::ocl::bitwise_and(gsrc1, gsrc2, gdst1, gmask); Near(0); } } TEST_P(Bitwise_and, Scalar) { - for(int j = 0; j < LOOP_TIMES; j++) + for (int j = 0; j < LOOP_TIMES; j++) { random_roi(); - cv::bitwise_and(mat1_roi, val, dst_roi); - cv::ocl::bitwise_and(gmat1, val, gdst); + cv::bitwise_and(src1_roi, val, dst1_roi); + cv::ocl::bitwise_and(gsrc1, val, gdst1); Near(1e-5); } } TEST_P(Bitwise_and, Scalar_Mask) { - for(int j = 0; j < LOOP_TIMES; j++) + for (int j = 0; j < LOOP_TIMES; j++) { random_roi(); - cv::bitwise_and(mat1_roi, val, dst_roi, mask_roi); - cv::ocl::bitwise_and(gmat1, val, gdst, gmask); + cv::bitwise_and(src1_roi, val, dst1_roi, mask_roi); + cv::ocl::bitwise_and(gsrc1, val, gdst1, gmask); Near(1e-5); } } +//////////////////////////////// Bitwise_or ///////////////////////////////////////////////// - -////////////////////////////////bitwise_or///////////////////////////////////////////////// - -struct Bitwise_or : ArithmTestBase {}; +typedef ArithmTestBase Bitwise_or; TEST_P(Bitwise_or, Mat) { - for(int j = 0; j < LOOP_TIMES; j++) + for (int j = 0; j < LOOP_TIMES; j++) { random_roi(); - cv::bitwise_or(mat1_roi, mat2_roi, dst_roi); - cv::ocl::bitwise_or(gmat1, gmat2, gdst); + cv::bitwise_or(src1_roi, src2_roi, dst1_roi); + cv::ocl::bitwise_or(gsrc1, gsrc2, gdst1); Near(0); } } TEST_P(Bitwise_or, Mat_Mask) { - for(int j = 0; j < LOOP_TIMES; j++) + for (int j = 0; j < LOOP_TIMES; j++) { random_roi(); - cv::bitwise_or(mat1_roi, mat2_roi, dst_roi, mask_roi); - cv::ocl::bitwise_or(gmat1, gmat2, gdst, gmask); + cv::bitwise_or(src1_roi, src2_roi, dst1_roi, mask_roi); + cv::ocl::bitwise_or(gsrc1, gsrc2, gdst1, gmask); Near(0); } } TEST_P(Bitwise_or, Scalar) { - for(int j = 0; j < LOOP_TIMES; j++) + for (int j = 0; j < LOOP_TIMES; j++) { random_roi(); - cv::bitwise_or(mat1_roi, val, dst_roi); - cv::ocl::bitwise_or(gmat1, val, gdst); + cv::bitwise_or(src1_roi, val, dst1_roi); + cv::ocl::bitwise_or(gsrc1, val, gdst1); Near(1e-5); } } TEST_P(Bitwise_or, Scalar_Mask) { - for(int j = 0; j < LOOP_TIMES; j++) + for (int j = 0; j < LOOP_TIMES; j++) { random_roi(); - cv::bitwise_or(mat1_roi, val, dst_roi, mask_roi); - cv::ocl::bitwise_or(gmat1, val, gdst, gmask); + cv::bitwise_or(src1_roi, val, dst1_roi, mask_roi); + cv::ocl::bitwise_or(gsrc1, val, gdst1, gmask); Near(1e-5); } } +//////////////////////////////// Bitwise_xor ///////////////////////////////////////////////// - -////////////////////////////////bitwise_xor///////////////////////////////////////////////// - -struct Bitwise_xor : ArithmTestBase {}; +typedef ArithmTestBase Bitwise_xor; TEST_P(Bitwise_xor, Mat) { - for(int j = 0; j < LOOP_TIMES; j++) + for (int j = 0; j < LOOP_TIMES; j++) { random_roi(); - cv::bitwise_xor(mat1_roi, mat2_roi, dst_roi); - cv::ocl::bitwise_xor(gmat1, gmat2, gdst); + cv::bitwise_xor(src1_roi, src2_roi, dst1_roi); + cv::ocl::bitwise_xor(gsrc1, gsrc2, gdst1); Near(0); } } TEST_P(Bitwise_xor, Mat_Mask) { - for(int j = 0; j < LOOP_TIMES; j++) + for (int j = 0; j < LOOP_TIMES; j++) { random_roi(); - cv::bitwise_xor(mat1_roi, mat2_roi, dst_roi, mask_roi); - cv::ocl::bitwise_xor(gmat1, gmat2, gdst, gmask); + cv::bitwise_xor(src1_roi, src2_roi, dst1_roi, mask_roi); + cv::ocl::bitwise_xor(gsrc1, gsrc2, gdst1, gmask); Near(0); } } TEST_P(Bitwise_xor, Scalar) { - for(int j = 0; j < LOOP_TIMES; j++) + for (int j = 0; j < LOOP_TIMES; j++) { random_roi(); - cv::bitwise_xor(mat1_roi, val, dst_roi); - cv::ocl::bitwise_xor(gmat1, val, gdst); + cv::bitwise_xor(src1_roi, val, dst1_roi); + cv::ocl::bitwise_xor(gsrc1, val, gdst1); Near(1e-5); } } TEST_P(Bitwise_xor, Scalar_Mask) { - for(int j = 0; j < LOOP_TIMES; j++) + for (int j = 0; j < LOOP_TIMES; j++) { random_roi(); - cv::bitwise_xor(mat1_roi, val, dst_roi, mask_roi); - cv::ocl::bitwise_xor(gmat1, val, gdst, gmask); + cv::bitwise_xor(src1_roi, val, dst1_roi, mask_roi); + cv::ocl::bitwise_xor(gsrc1, val, gdst1, gmask); Near(1e-5); } } +//////////////////////////////// Bitwise_not ///////////////////////////////////////////////// -////////////////////////////////bitwise_not///////////////////////////////////////////////// - -struct Bitwise_not : ArithmTestBase {}; +typedef ArithmTestBase Bitwise_not; TEST_P(Bitwise_not, Mat) { - for(int j = 0; j < LOOP_TIMES; j++) + for (int j = 0; j < LOOP_TIMES; j++) { random_roi(); - cv::bitwise_not(mat1_roi, dst_roi); - cv::ocl::bitwise_not(gmat1, gdst); + cv::bitwise_not(src1_roi, dst1_roi); + cv::ocl::bitwise_not(gsrc1, gdst1); Near(0); } } +//////////////////////////////// Compare ///////////////////////////////////////////////// -////////////////////////////////compare///////////////////////////////////////////////// -struct Compare : ArithmTestBase {}; +typedef ArithmTestBase Compare; TEST_P(Compare, Mat) { - if(mat1.type() == CV_8SC1) - //if(mat1.type() != CV_8UC1 || mat1.type()!= CV_16UC1 || mat1.type()!= CV_16SC1|| mat1.type()!= CV_32SC1 || mat1.type()!= CV_32FC1|| mat1.type()!= CV_64FC1) - { - cout << "\tUnsupported type\t\n"; - } - - int cmp_codes[] = {CMP_EQ, CMP_GT, CMP_GE, CMP_LT, CMP_LE, CMP_NE}; - //const char *cmp_str[] = {"CMP_EQ", "CMP_GT", "CMP_GE", "CMP_LT", "CMP_LE", "CMP_NE"}; + int cmp_codes[] = { CMP_EQ, CMP_GT, CMP_GE, CMP_LT, CMP_LE, CMP_NE }; int cmp_num = sizeof(cmp_codes) / sizeof(int); for (int i = 0; i < cmp_num; ++i) - { - - for(int j = 0; j < LOOP_TIMES; j++) + for (int j = 0; j < LOOP_TIMES; j++) { random_roi(); - cv::compare(mat1_roi, mat2_roi, dst_roi, cmp_codes[i]); - cv::ocl::compare(gmat1, gmat2, gdst, cmp_codes[i]); + cv::compare(src1_roi, src2_roi, dst1_roi, cmp_codes[i]); + cv::ocl::compare(gsrc1, gsrc2, gdst1, cmp_codes[i]); + Near(0); } - } - } +//////////////////////////////// Pow ///////////////////////////////////////////////// -struct Pow : ArithmTestBase {}; +typedef ArithmTestBase Pow; TEST_P(Pow, Mat) { - if(mat1.depth() != CV_32F && mat1.depth() != CV_64F) - { - cout << "\tUnsupported type\t\n"; - } - - for(int j = 0; j < LOOP_TIMES; j++) + for (int j = 0; j < LOOP_TIMES; j++) { random_roi(); double p = 4.5; - cv::pow(mat1_roi, p, dst_roi); - cv::ocl::pow(gmat1, p, gdst); + cv::pow(src1_roi, p, dst1_roi); + cv::ocl::pow(gsrc1, p, gdst1); Near(1); } } +//////////////////////////////// AddWeighted ///////////////////////////////////////////////// -struct AddWeighted : ArithmTestBase {}; +typedef ArithmTestBase AddWeighted; TEST_P(AddWeighted, Mat) { - for(int j = 0; j < LOOP_TIMES; j++) + for (int j = 0; j < LOOP_TIMES; j++) { random_roi(); - double alpha = 2.0, beta = 1.0, gama = 3.0; - - - cv::addWeighted(mat1_roi, alpha, mat2_roi, beta, gama, dst_roi); - // cv::ocl::oclMat clmat1(mat1),clmat2(mat2),cldst; + const double alpha = 2.0, beta = 1.0, gama = 3.0; - cv::ocl::addWeighted(gmat1, alpha, gmat2, beta, gama, gdst); + cv::addWeighted(src1_roi, alpha, src2_roi, beta, gama, dst1_roi); + cv::ocl::addWeighted(gsrc1, alpha, gsrc2, beta, gama, gdst1); Near(1e-5); } } - - - -//********test**************** - -INSTANTIATE_TEST_CASE_P(Arithm, Lut, Combine( - Values(CV_8UC1, CV_8UC3, CV_8UC4), - Values(false))); // Values(false) is the reserved parameter - -INSTANTIATE_TEST_CASE_P(Arithm, Exp, Combine( - Values(CV_32FC1, CV_32FC1), - Values(false))); // Values(false) is the reserved parameter - -INSTANTIATE_TEST_CASE_P(Arithm, Log, Combine( - Values(CV_32FC1, CV_32FC1), - Values(false))); // Values(false) is the reserved parameter - -INSTANTIATE_TEST_CASE_P(Arithm, Add, Combine( - Values(CV_8UC1, CV_8UC3, CV_8UC4, CV_32SC1, CV_32SC3, CV_32SC4, CV_32FC1, CV_32FC3, CV_32FC4), - Values(false))); - -INSTANTIATE_TEST_CASE_P(Arithm, Sub, Combine( - Values(CV_8UC1, CV_8UC3, CV_8UC4, CV_32SC1, CV_32SC3, CV_32SC4, CV_32FC1, CV_32FC3, CV_32FC4), - Values(false))); - -INSTANTIATE_TEST_CASE_P(Arithm, Mul, Combine( - Values(CV_8UC1, CV_8UC3, CV_8UC4, CV_32SC1, CV_32SC3, CV_32SC4, CV_32FC1, CV_32FC3, CV_32FC4), - Values(false))); // Values(false) is the reserved parameter - -INSTANTIATE_TEST_CASE_P(Arithm, Div, Combine( - Values(CV_8UC1, CV_8UC3, CV_8UC4, CV_32SC1, CV_32SC3, CV_32SC4, CV_32FC1, CV_32FC3, CV_32FC4), - Values(false))); // Values(false) is the reserved parameter - - -INSTANTIATE_TEST_CASE_P(Arithm, Absdiff, Combine( - Values(CV_8UC1, CV_8UC3, CV_8UC4, CV_32SC1, CV_32SC3, CV_32SC4, CV_32FC1, CV_32FC3, CV_32FC4), - Values(false))); // Values(false) is the reserved parameter - -INSTANTIATE_TEST_CASE_P(Arithm, CartToPolar, Combine( - Values(CV_32FC1, CV_32FC3, CV_32FC4), - Values(false))); // Values(false) is the reserved parameter - -INSTANTIATE_TEST_CASE_P(Arithm, PolarToCart, Combine( - Values(CV_32FC1, CV_32FC3, CV_32FC4), - Values(false))); // Values(false) is the reserved parameter - -INSTANTIATE_TEST_CASE_P(Arithm, Magnitude, Combine( - Values(CV_32FC1, CV_32FC3, CV_32FC4), - Values(false))); // Values(false) is the reserved parameter - -INSTANTIATE_TEST_CASE_P(Arithm, Transpose, Combine( - Values(CV_8UC1, CV_8UC3, CV_8UC4, CV_32SC1, CV_32FC1), - Values(false))); // Values(false) is the reserved parameter - -INSTANTIATE_TEST_CASE_P(Arithm, Flip, Combine( - Values(CV_8UC1, CV_8UC3, CV_8UC4, CV_32SC1, CV_32SC3, CV_32SC4, CV_32FC1, CV_32FC3, CV_32FC4), - Values(false))); // Values(false) is the reserved parameter - -INSTANTIATE_TEST_CASE_P(Arithm, MinMax, Combine( - Values(CV_8UC1, CV_32SC1, CV_32FC1), - Values(false))); - -INSTANTIATE_TEST_CASE_P(Arithm, MinMaxLoc, Combine( - Values(CV_8UC1, CV_32SC1, CV_32FC1), - Values(false))); - -INSTANTIATE_TEST_CASE_P(Arithm, Sum, Combine( - Values(CV_8U, CV_32S, CV_32F), - Values(false))); - -INSTANTIATE_TEST_CASE_P(Arithm, CountNonZero, Combine( - Values(CV_8U, CV_32S, CV_32F), - Values(false))); - - -INSTANTIATE_TEST_CASE_P(Arithm, Phase, Combine(Values(CV_32FC1, CV_32FC3, CV_32FC4), Values(false))); -// Values(false) is the reserved parameter - - -INSTANTIATE_TEST_CASE_P(Arithm, Bitwise_and, Combine( - Values(CV_8UC1, CV_8UC3, CV_8UC4, CV_32SC1, CV_32SC3, CV_32SC4, CV_32FC1, CV_32FC3, CV_32FC4), Values(false))); -//Values(false) is the reserved parameter - -INSTANTIATE_TEST_CASE_P(Arithm, Bitwise_or, Combine( - Values(CV_8UC1, CV_8UC3, CV_8UC4, CV_32SC1, CV_32FC1, CV_32FC3, CV_32FC4), Values(false))); -//Values(false) is the reserved parameter - -INSTANTIATE_TEST_CASE_P(Arithm, Bitwise_xor, Combine( - Values(CV_8UC1, CV_8UC3, CV_8UC4, CV_32SC1, CV_32FC1, CV_32FC3, CV_32FC4), Values(false))); -//Values(false) is the reserved parameter - -INSTANTIATE_TEST_CASE_P(Arithm, Bitwise_not, Combine( - Values(CV_8UC1, CV_8UC3, CV_8UC4, CV_32SC1, CV_32FC1, CV_32FC3, CV_32FC4), Values(false))); -//Values(false) is the reserved parameter - -INSTANTIATE_TEST_CASE_P(Arithm, Compare, Combine(Values(CV_8UC1, CV_32SC1, CV_32FC1), Values(false))); -// Values(false) is the reserved parameter - -INSTANTIATE_TEST_CASE_P(Arithm, Pow, Combine(Values(CV_32FC1, CV_32FC3, CV_32FC4), Values(false))); -// Values(false) is the reserved parameter - -INSTANTIATE_TEST_CASE_P(Arithm, AddWeighted, Combine( - Values(CV_8UC1, CV_32SC1, CV_32FC1), - Values(false))); // Values(false) is the reserved parameter - - +//////////////////////////////////////// Instantiation ///////////////////////////////////////// + +INSTANTIATE_TEST_CASE_P(Arithm, Lut, Combine(testing::Range(CV_8U, CV_USRTYPE1), testing::Range(1, 5), Bool(), Bool())); // + +INSTANTIATE_TEST_CASE_P(Arithm, Exp, Combine(testing::Values(CV_32F, CV_64F), testing::Range(1, 5), Bool())); // + +INSTANTIATE_TEST_CASE_P(Arithm, Log, Combine(testing::Values(CV_32F, CV_64F), testing::Range(1, 5), Bool())); // + +INSTANTIATE_TEST_CASE_P(Arithm, Add, Combine(testing::Range(CV_8U, CV_USRTYPE1), testing::Range(1, 5), Bool())); // + +INSTANTIATE_TEST_CASE_P(Arithm, Sub, Combine(testing::Range(CV_8U, CV_USRTYPE1), testing::Range(1, 5), Bool())); // + +INSTANTIATE_TEST_CASE_P(Arithm, Mul, Combine(testing::Range(CV_8U, CV_USRTYPE1), testing::Range(1, 5), Bool())); +INSTANTIATE_TEST_CASE_P(Arithm, Div, Combine(testing::Range(CV_8U, CV_USRTYPE1), testing::Range(1, 5), Bool())); +INSTANTIATE_TEST_CASE_P(Arithm, Absdiff, Combine(testing::Range(CV_8U, CV_USRTYPE1), testing::Range(1, 5), Bool())); // + +INSTANTIATE_TEST_CASE_P(Arithm, CartToPolar, Combine(Values(CV_32F, CV_64F), testing::Range(1, 5), Bool())); // + +INSTANTIATE_TEST_CASE_P(Arithm, PolarToCart, Combine(Values(CV_32F, CV_64F), testing::Range(1, 5), Bool())); // + +INSTANTIATE_TEST_CASE_P(Arithm, Magnitude, Combine(Values(CV_32F, CV_64F), testing::Range(1, 5), Bool())); // + +INSTANTIATE_TEST_CASE_P(Arithm, Transpose, Combine(testing::Range(CV_8U, CV_USRTYPE1), testing::Range(1, 5), Bool())); // + +INSTANTIATE_TEST_CASE_P(Arithm, Flip, Combine(testing::Range(CV_8U, CV_USRTYPE1), testing::Range(1, 5), Bool())); // + +INSTANTIATE_TEST_CASE_P(Arithm, MinMax, Combine(testing::Range(CV_8U, CV_USRTYPE1), Values(1), Bool())); +INSTANTIATE_TEST_CASE_P(Arithm, MinMaxLoc, Combine(testing::Range(CV_8U, CV_USRTYPE1), Values(1), Bool())); // + +INSTANTIATE_TEST_CASE_P(Arithm, Sum, Combine(testing::Range(CV_8U, CV_USRTYPE1), testing::Range(1, 5), Bool())); +INSTANTIATE_TEST_CASE_P(Arithm, CountNonZero, Combine(testing::Range(CV_8U, CV_USRTYPE1), Values(1), Bool())); // + +INSTANTIATE_TEST_CASE_P(Arithm, Phase, Combine(Values(CV_32F, CV_64F), testing::Range(1, 5), Bool())); // + +INSTANTIATE_TEST_CASE_P(Arithm, Bitwise_and, Combine(testing::Range(CV_8U, CV_USRTYPE1), testing::Range(1, 5), Bool())); // + +INSTANTIATE_TEST_CASE_P(Arithm, Bitwise_or, Combine(testing::Range(CV_8U, CV_USRTYPE1), testing::Range(1, 5), Bool())); // + +INSTANTIATE_TEST_CASE_P(Arithm, Bitwise_xor, Combine(testing::Range(CV_8U, CV_USRTYPE1), testing::Range(1, 5), Bool())); // + +INSTANTIATE_TEST_CASE_P(Arithm, Bitwise_not, Combine(testing::Range(CV_8U, CV_USRTYPE1), testing::Range(1, 5), Bool())); // + +INSTANTIATE_TEST_CASE_P(Arithm, Compare, Combine(testing::Range(CV_8U, CV_USRTYPE1), Values(1), Bool())); // + +INSTANTIATE_TEST_CASE_P(Arithm, Pow, Combine(Values(CV_32F, CV_64F), testing::Range(1, 5), Bool())); // + +INSTANTIATE_TEST_CASE_P(Arithm, AddWeighted, Combine(testing::Range(CV_8U, CV_USRTYPE1), testing::Range(1, 5), Bool())); // + #endif // HAVE_OPENCL From c87d2d414d253385d77652171b13e6ad2bcae3cb Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Tue, 24 Sep 2013 14:18:42 +0400 Subject: [PATCH 49/68] removed arithm_absdiff.cl --- modules/ocl/src/opencl/arithm_absdiff.cl | 970 ----------------------- 1 file changed, 970 deletions(-) delete mode 100644 modules/ocl/src/opencl/arithm_absdiff.cl diff --git a/modules/ocl/src/opencl/arithm_absdiff.cl b/modules/ocl/src/opencl/arithm_absdiff.cl deleted file mode 100644 index 341a0048ff..0000000000 --- a/modules/ocl/src/opencl/arithm_absdiff.cl +++ /dev/null @@ -1,970 +0,0 @@ -/*M/////////////////////////////////////////////////////////////////////////////////////// -// -// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. -// -// By downloading, copying, installing or using the software you agree to this license. -// If you do not agree to this license, do not download, install, -// copy or use the software. -// -// -// License Agreement -// For Open Source Computer Vision Library -// -// Copyright (C) 2010-2012, Institute Of Software Chinese Academy Of Science, all rights reserved. -// Copyright (C) 2010-2012, Advanced Micro Devices, Inc., all rights reserved. -// Third party copyrights are property of their respective owners. -// -// @Authors -// Jia Haipeng, jiahaipeng95@gmail.com -// -// Redistribution and use in source and binary forms, with or without modification, -// are permitted provided that the following conditions are met: -// -// * Redistribution's of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// * Redistribution's in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other oclMaterials provided with the distribution. -// -// * The name of the copyright holders may not be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// This software is provided by the copyright holders and contributors as is and -// any express or implied warranties, including, but not limited to, the implied -// warranties of merchantability and fitness for a particular purpose are disclaimed. -// In no event shall the Intel Corporation or contributors be liable for any direct, -// indirect, incidental, special, exemplary, or consequential damages -// (including, but not limited to, procurement of substitute goods or services; -// loss of use, data, or profits; or business interruption) however caused -// and on any theory of liability, whether in contract, strict liability, -// or tort (including negligence or otherwise) arising in any way out of -// the use of this software, even if advised of the possibility of such damage. -// -//M*/ - -#if defined (DOUBLE_SUPPORT) -#ifdef cl_khr_fp64 -#pragma OPENCL EXTENSION cl_khr_fp64:enable -#elif defined (cl_amd_fp64) -#pragma OPENCL EXTENSION cl_amd_fp64:enable -#endif -#endif - -////////////////////////////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////absdiff//////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////////////////////////////// -/**************************************adddiff *************************************/ -__kernel void arithm_absdiff_D0 (__global uchar *src1, int src1_step, int src1_offset, - __global uchar *src2, int src2_step, int src2_offset, - __global uchar *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 2; - -#ifdef dst_align -#undef dst_align -#endif - #define dst_align (dst_offset & 3) - int src1_index = mad24(y, src1_step, x + src1_offset - dst_align); - int src2_index = mad24(y, src2_step, x + src2_offset - dst_align); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + x & (int)0xfffffffc); - int src1_index_fix = src1_index < 0 ? 0 : src1_index; - int src2_index_fix = src2_index < 0 ? 0 : src2_index; - uchar4 src1_data = vload4(0, src1 + src1_index_fix); - uchar4 src2_data = vload4(0, src2 + src2_index_fix); - if(src1_index < 0) - { - uchar4 tmp; - tmp.xyzw = (src1_index == -2) ? src1_data.zwxy:src1_data.yzwx; - src1_data.xyzw = (src1_index == -1) ? src1_data.wxyz:tmp.xyzw; - } - if(src2_index < 0) - { - uchar4 tmp; - tmp.xyzw = (src2_index == -2) ? src2_data.zwxy:src2_data.yzwx; - src2_data.xyzw = (src2_index == -1) ? src2_data.wxyz:tmp.xyzw; - } - - uchar4 dst_data = *((__global uchar4 *)(dst + dst_index)); - uchar4 tmp_data = abs_diff(src1_data, src2_data); - - dst_data.x = ((dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : dst_data.x; - dst_data.y = ((dst_index + 1 >= dst_start) && (dst_index + 1 < dst_end)) ? tmp_data.y : dst_data.y; - dst_data.z = ((dst_index + 2 >= dst_start) && (dst_index + 2 < dst_end)) ? tmp_data.z : dst_data.z; - dst_data.w = ((dst_index + 3 >= dst_start) && (dst_index + 3 < dst_end)) ? tmp_data.w : dst_data.w; - - *((__global uchar4 *)(dst + dst_index)) = dst_data; - } -} -__kernel void arithm_absdiff_D2 (__global ushort *src1, int src1_step, int src1_offset, - __global ushort *src2, int src2_step, int src2_offset, - __global ushort *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) - -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 2; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align ((dst_offset >> 1) & 3) - int src1_index = mad24(y, src1_step, (x << 1) + src1_offset - (dst_align << 1)); - int src2_index = mad24(y, src2_step, (x << 1) + src2_offset - (dst_align << 1)); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + (x << 1) & (int)0xfffffff8); - - ushort4 src1_data = vload4(0, (__global ushort *)((__global char *)src1 + src1_index)); - ushort4 src2_data = vload4(0, (__global ushort *)((__global char *)src2 + src2_index)); - - ushort4 dst_data = *((__global ushort4 *)((__global char *)dst + dst_index)); - ushort4 tmp_data = abs_diff(src1_data, src2_data); - - dst_data.x = ((dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : dst_data.x; - dst_data.y = ((dst_index + 2 >= dst_start) && (dst_index + 2 < dst_end)) ? tmp_data.y : dst_data.y; - dst_data.z = ((dst_index + 4 >= dst_start) && (dst_index + 4 < dst_end)) ? tmp_data.z : dst_data.z; - dst_data.w = ((dst_index + 6 >= dst_start) && (dst_index + 6 < dst_end)) ? tmp_data.w : dst_data.w; - - *((__global ushort4 *)((__global char *)dst + dst_index)) = dst_data; - } -} -__kernel void arithm_absdiff_D3 (__global short *src1, int src1_step, int src1_offset, - __global short *src2, int src2_step, int src2_offset, - __global short *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 2; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align ((dst_offset >> 1) & 3) - int src1_index = mad24(y, src1_step, (x << 1) + src1_offset - (dst_align << 1)); - int src2_index = mad24(y, src2_step, (x << 1) + src2_offset - (dst_align << 1)); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + (x << 1) & (int)0xfffffff8); - - short4 src1_data = vload4(0, (__global short *)((__global char *)src1 + src1_index)); - short4 src2_data = vload4(0, (__global short *)((__global char *)src2 + src2_index)); - - short4 dst_data = *((__global short4 *)((__global char *)dst + dst_index)); - ushort4 tmp = abs_diff(src1_data, src2_data); - short4 tmp_data = convert_short4_sat(tmp); - - dst_data.x = ((dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : dst_data.x; - dst_data.y = ((dst_index + 2 >= dst_start) && (dst_index + 2 < dst_end)) ? tmp_data.y : dst_data.y; - dst_data.z = ((dst_index + 4 >= dst_start) && (dst_index + 4 < dst_end)) ? tmp_data.z : dst_data.z; - dst_data.w = ((dst_index + 6 >= dst_start) && (dst_index + 6 < dst_end)) ? tmp_data.w : dst_data.w; - - *((__global short4 *)((__global char *)dst + dst_index)) = dst_data; - } -} - -__kernel void arithm_absdiff_D4 (__global int *src1, int src1_step, int src1_offset, - __global int *src2, int src2_step, int src2_offset, - __global int *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 2) + src1_offset); - int src2_index = mad24(y, src2_step, (x << 2) + src2_offset); - int dst_index = mad24(y, dst_step, (x << 2) + dst_offset); - - int data1 = *((__global int *)((__global char *)src1 + src1_index)); - int data2 = *((__global int *)((__global char *)src2 + src2_index)); - uint tmp = abs_diff(data1, data2); - int tmp_data = convert_int_sat(tmp); - - *((__global int *)((__global char *)dst + dst_index)) = tmp_data; - } -} -__kernel void arithm_absdiff_D5 (__global float *src1, int src1_step, int src1_offset, - __global float *src2, int src2_step, int src2_offset, - __global float *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 2) + src1_offset); - int src2_index = mad24(y, src2_step, (x << 2) + src2_offset); - int dst_index = mad24(y, dst_step, (x << 2) + dst_offset); - - float data1 = *((__global float *)((__global char *)src1 + src1_index)); - float data2 = *((__global float *)((__global char *)src2 + src2_index)); - float tmp = fabs(data1 - data2); - - *((__global float *)((__global char *)dst + dst_index)) = tmp; - } -} - -#if defined (DOUBLE_SUPPORT) -__kernel void arithm_absdiff_D6 (__global double *src1, int src1_step, int src1_offset, - __global double *src2, int src2_step, int src2_offset, - __global double *dst, int dst_step, int dst_offset, - int rows, int cols, int dst_step1) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 3) + src1_offset); - int src2_index = mad24(y, src2_step, (x << 3) + src2_offset); - int dst_index = mad24(y, dst_step, (x << 3) + dst_offset); - - double data1 = *((__global double *)((__global char *)src1 + src1_index)); - double data2 = *((__global double *)((__global char *)src2 + src2_index)); - double tmp = fabs(data1-data2); - - *((__global double *)((__global char *)dst + dst_index)) = tmp; - } -} -#endif - -/**************************************absdiff with scalar**************************************/ -__kernel void arithm_s_absdiff_C1_D0 (__global uchar *src1, int src1_step, int src1_offset, - __global uchar *dst, int dst_step, int dst_offset, - int4 src2, int rows, int cols, int dst_step1) -{ - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 2; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align (dst_offset & 3) - int src1_index = mad24(y, src1_step, x + src1_offset - dst_align); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + x & (int)0xfffffffc); - int src1_index_fix = src1_index < 0 ? 0 : src1_index; - uchar4 src1_data = vload4(0, src1 + src1_index_fix); - int4 src2_data = (int4)(src2.x, src2.x, src2.x, src2.x); - if(src1_index < 0) - { - uchar4 tmp; - tmp.xyzw = (src1_index == -2) ? src1_data.zwxy:src1_data.yzwx; - src1_data.xyzw = (src1_index == -1) ? src1_data.wxyz:tmp.xyzw; - } - - uchar4 data = *((__global uchar4 *)(dst + dst_index)); - uchar4 tmp_data = convert_uchar4_sat(abs_diff(convert_int4_sat(src1_data), src2_data)); - - data.x = ((dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) ? tmp_data.x : data.x; - data.y = ((dst_index + 1 >= dst_start) && (dst_index + 1 < dst_end)) ? tmp_data.y : data.y; - data.z = ((dst_index + 2 >= dst_start) && (dst_index + 2 < dst_end)) ? tmp_data.z : data.z; - data.w = ((dst_index + 3 >= dst_start) && (dst_index + 3 < dst_end)) ? tmp_data.w : data.w; - - *((__global uchar4 *)(dst + dst_index)) = data; - } -} -__kernel void arithm_s_absdiff_C1_D2 (__global ushort *src1, int src1_step, int src1_offset, - __global ushort *dst, int dst_step, int dst_offset, - int4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 1; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align ((dst_offset >> 1) & 1) - int src1_index = mad24(y, src1_step, (x << 1) + src1_offset - (dst_align << 1)); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + (x << 1) & (int)0xfffffffc); - - ushort2 src1_data = vload2(0, (__global ushort *)((__global char *)src1 + src1_index)); - int2 src2_data = (int2)(src2.x, src2.x); - - ushort2 data = *((__global ushort2 *)((__global uchar *)dst + dst_index)); - ushort2 tmp_data = convert_ushort2_sat(abs_diff(convert_int2_sat(src1_data), src2_data)); - - data.x = (dst_index + 0 >= dst_start) ? tmp_data.x : data.x; - data.y = (dst_index + 2 < dst_end ) ? tmp_data.y : data.y; - - *((__global ushort2 *)((__global uchar *)dst + dst_index)) = data; - } -} -__kernel void arithm_s_absdiff_C1_D3 (__global short *src1, int src1_step, int src1_offset, - __global short *dst, int dst_step, int dst_offset, - int4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 1; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align ((dst_offset >> 1) & 1) - int src1_index = mad24(y, src1_step, (x << 1) + src1_offset - (dst_align << 1)); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + (x << 1) & (int)0xfffffffc); - - short2 src1_data = vload2(0, (__global short *)((__global char *)src1 + src1_index)); - int2 src2_data = (int2)(src2.x, src2.x); - short2 data = *((__global short2 *)((__global uchar *)dst + dst_index)); - - ushort2 tmp = convert_ushort2_sat(abs_diff(convert_int2_sat(src1_data), src2_data)); - short2 tmp_data = convert_short2_sat(tmp); - - data.x = (dst_index + 0 >= dst_start) ? tmp_data.x : data.x; - data.y = (dst_index + 2 < dst_end ) ? tmp_data.y : data.y; - - *((__global short2 *)((__global uchar *)dst + dst_index)) = data; - } -} -__kernel void arithm_s_absdiff_C1_D4 (__global int *src1, int src1_step, int src1_offset, - __global int *dst, int dst_step, int dst_offset, - int4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 2) + src1_offset); - int dst_index = mad24(y, dst_step, (x << 2) + dst_offset); - - int src_data1 = *((__global int *)((__global char *)src1 + src1_index)); - int src_data2 = src2.x; - int dst_data = *((__global int *)((__global char *)dst + dst_index)); - - uint tmp_data = abs_diff(src_data1, src_data2); - int data = convert_int_sat(tmp_data); - - *((__global int *)((__global char *)dst + dst_index)) = data; - } -} -__kernel void arithm_s_absdiff_C1_D5 (__global float *src1, int src1_step, int src1_offset, - __global float *dst, int dst_step, int dst_offset, - float4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 2) + src1_offset); - int dst_index = mad24(y, dst_step, (x << 2) + dst_offset); - - float src_data1 = *((__global float *)((__global char *)src1 + src1_index)); - float src_data2 = src2.x; - float dst_data = *((__global float *)((__global char *)dst + dst_index)); - - float data = fabs(src_data1 - src_data2); - - *((__global float *)((__global char *)dst + dst_index)) = data; - } -} - -#if defined (DOUBLE_SUPPORT) -__kernel void arithm_s_absdiff_C1_D6 (__global double *src1, int src1_step, int src1_offset, - __global double *dst, int dst_step, int dst_offset, - double4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 3) + src1_offset); - int dst_index = mad24(y, dst_step, (x << 3) + dst_offset); - - double src_data1 = *((__global double *)((__global char *)src1 + src1_index)); - double src2_data = src2.x; - double dst_data = *((__global double *)((__global char *)dst + dst_index)); - - double data = fabs(src_data1 - src2_data); - - *((__global double *)((__global char *)dst + dst_index)) = data; - } -} -#endif - -__kernel void arithm_s_absdiff_C2_D0 (__global uchar *src1, int src1_step, int src1_offset, - __global uchar *dst, int dst_step, int dst_offset, - int4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 1; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align ((dst_offset >> 1) & 1) - int src1_index = mad24(y, src1_step, (x << 1) + src1_offset - (dst_align << 1)); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + (x << 1) & (int)0xfffffffc); - - uchar4 src1_data = vload4(0, src1 + src1_index); - int4 src2_data = (int4)(src2.x, src2.y, src2.x, src2.y); - - uchar4 data = *((__global uchar4 *)(dst + dst_index)); - uchar4 tmp_data = convert_uchar4_sat(abs_diff(convert_int4_sat(src1_data), src2_data)); - - data.xy = (dst_index + 0 >= dst_start) ? tmp_data.xy : data.xy; - data.zw = (dst_index + 2 < dst_end ) ? tmp_data.zw : data.zw; - - *((__global uchar4 *)(dst + dst_index)) = data; - } -} -__kernel void arithm_s_absdiff_C2_D2 (__global ushort *src1, int src1_step, int src1_offset, - __global ushort *dst, int dst_step, int dst_offset, - int4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 2) + src1_offset); - int dst_index = mad24(y, dst_step, (x << 2) + dst_offset); - - ushort2 src_data1 = *((__global ushort2 *)((__global char *)src1 + src1_index)); - int2 src_data2 = (int2)(src2.x, src2.y); - ushort2 dst_data = *((__global ushort2 *)((__global char *)dst + dst_index)); - - ushort2 data = convert_ushort2_sat( abs_diff(convert_int2_sat(src_data1), src_data2)); - - *((__global ushort2 *)((__global char *)dst + dst_index)) = data; - } -} -__kernel void arithm_s_absdiff_C2_D3 (__global short *src1, int src1_step, int src1_offset, - __global short *dst, int dst_step, int dst_offset, - int4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 2) + src1_offset); - int dst_index = mad24(y, dst_step, (x << 2) + dst_offset); - - short2 src_data1 = *((__global short2 *)((__global char *)src1 + src1_index)); - int2 src_data2 = (int2)(src2.x, src2.y); - short2 dst_data = *((__global short2 *)((__global char *)dst + dst_index)); - - ushort2 tmp = convert_ushort2_sat(abs_diff(convert_int2_sat(src_data1), src_data2)); - short2 data = convert_short2_sat(tmp); - - *((__global short2 *)((__global char *)dst + dst_index)) = data; - } -} -__kernel void arithm_s_absdiff_C2_D4 (__global int *src1, int src1_step, int src1_offset, - __global int *dst, int dst_step, int dst_offset, - int4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 3) + src1_offset); - int dst_index = mad24(y, dst_step, (x << 3) + dst_offset); - - int2 src_data1 = *((__global int2 *)((__global char *)src1 + src1_index)); - int2 src_data2 = (int2)(src2.x, src2.y); - int2 dst_data = *((__global int2 *)((__global char *)dst + dst_index)); - - int2 data = convert_int2_sat(abs_diff(src_data1, src_data2)); - *((__global int2 *)((__global char *)dst + dst_index)) = data; - } -} -__kernel void arithm_s_absdiff_C2_D5 (__global float *src1, int src1_step, int src1_offset, - __global float *dst, int dst_step, int dst_offset, - float4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 3) + src1_offset); - int dst_index = mad24(y, dst_step, (x << 3) + dst_offset); - - float2 src_data1 = *((__global float2 *)((__global char *)src1 + src1_index)); - float2 src_data2 = (float2)(src2.x, src2.y); - float2 dst_data = *((__global float2 *)((__global char *)dst + dst_index)); - - float2 data = fabs(src_data1 - src_data2); - *((__global float2 *)((__global char *)dst + dst_index)) = data; - } -} -#if defined (DOUBLE_SUPPORT) -__kernel void arithm_s_absdiff_C2_D6 (__global double *src1, int src1_step, int src1_offset, - __global double *dst, int dst_step, int dst_offset, - double4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 4) + src1_offset); - int dst_index = mad24(y, dst_step, (x << 4) + dst_offset); - - double2 src_data1 = *((__global double2 *)((__global char *)src1 + src1_index)); - double2 src_data2 = (double2)(src2.x, src2.y); - double2 dst_data = *((__global double2 *)((__global char *)dst + dst_index)); - - double2 data = fabs(src_data1 - src_data2); - - *((__global double2 *)((__global char *)dst + dst_index)) = data; - } -} -#endif -__kernel void arithm_s_absdiff_C3_D0 (__global uchar *src1, int src1_step, int src1_offset, - __global uchar *dst, int dst_step, int dst_offset, - int4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 2; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align (((dst_offset % dst_step) / 3 ) & 3) - int src1_index = mad24(y, src1_step, (x * 3) + src1_offset - (dst_align * 3)); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + (x * 3) - (dst_align * 3)); - - uchar4 src1_data_0 = vload4(0, src1 + src1_index + 0); - uchar4 src1_data_1 = vload4(0, src1 + src1_index + 4); - uchar4 src1_data_2 = vload4(0, src1 + src1_index + 8); - - int4 src2_data_0 = (int4)(src2.x, src2.y, src2.z, src2.x); - int4 src2_data_1 = (int4)(src2.y, src2.z, src2.x, src2.y); - int4 src2_data_2 = (int4)(src2.z, src2.x, src2.y, src2.z); - - uchar4 data_0 = *((__global uchar4 *)(dst + dst_index + 0)); - uchar4 data_1 = *((__global uchar4 *)(dst + dst_index + 4)); - uchar4 data_2 = *((__global uchar4 *)(dst + dst_index + 8)); - - uchar4 tmp_data_0 = convert_uchar4_sat(abs_diff(convert_int4_sat(src1_data_0), src2_data_0)); - uchar4 tmp_data_1 = convert_uchar4_sat(abs_diff(convert_int4_sat(src1_data_1), src2_data_1)); - uchar4 tmp_data_2 = convert_uchar4_sat(abs_diff(convert_int4_sat(src1_data_2), src2_data_2)); - - data_0.xyz = ((dst_index + 0 >= dst_start)) ? tmp_data_0.xyz : data_0.xyz; - data_0.w = ((dst_index + 3 >= dst_start) && (dst_index + 3 < dst_end)) - ? tmp_data_0.w : data_0.w; - - data_1.xy = ((dst_index + 3 >= dst_start) && (dst_index + 3 < dst_end)) - ? tmp_data_1.xy : data_1.xy; - data_1.zw = ((dst_index + 6 >= dst_start) && (dst_index + 6 < dst_end)) - ? tmp_data_1.zw : data_1.zw; - - data_2.x = ((dst_index + 6 >= dst_start) && (dst_index + 6 < dst_end)) - ? tmp_data_2.x : data_2.x; - data_2.yzw = ((dst_index + 9 >= dst_start) && (dst_index + 9 < dst_end)) - ? tmp_data_2.yzw : data_2.yzw; - - *((__global uchar4 *)(dst + dst_index + 0)) = data_0; - *((__global uchar4 *)(dst + dst_index + 4)) = data_1; - *((__global uchar4 *)(dst + dst_index + 8)) = data_2; - } -} -__kernel void arithm_s_absdiff_C3_D2 (__global ushort *src1, int src1_step, int src1_offset, - __global ushort *dst, int dst_step, int dst_offset, - int4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 1; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align (((dst_offset % dst_step) / 6 ) & 1) - int src1_index = mad24(y, src1_step, (x * 6) + src1_offset - (dst_align * 6)); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + (x * 6) - (dst_align * 6)); - - ushort2 src1_data_0 = vload2(0, (__global ushort *)((__global char *)src1 + src1_index + 0)); - ushort2 src1_data_1 = vload2(0, (__global ushort *)((__global char *)src1 + src1_index + 4)); - ushort2 src1_data_2 = vload2(0, (__global ushort *)((__global char *)src1 + src1_index + 8)); - - int2 src2_data_0 = (int2)(src2.x, src2.y); - int2 src2_data_1 = (int2)(src2.z, src2.x); - int2 src2_data_2 = (int2)(src2.y, src2.z); - - ushort2 data_0 = *((__global ushort2 *)((__global char *)dst + dst_index + 0)); - ushort2 data_1 = *((__global ushort2 *)((__global char *)dst + dst_index + 4)); - ushort2 data_2 = *((__global ushort2 *)((__global char *)dst + dst_index + 8)); - - ushort2 tmp_data_0 = convert_ushort2_sat(abs_diff(convert_int2_sat(src1_data_0), src2_data_0)); - ushort2 tmp_data_1 = convert_ushort2_sat(abs_diff(convert_int2_sat(src1_data_1), src2_data_1)); - ushort2 tmp_data_2 = convert_ushort2_sat(abs_diff(convert_int2_sat(src1_data_2), src2_data_2)); - - data_0.xy = ((dst_index + 0 >= dst_start)) ? tmp_data_0.xy : data_0.xy; - - data_1.x = ((dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) - ? tmp_data_1.x : data_1.x; - data_1.y = ((dst_index + 6 >= dst_start) && (dst_index + 6 < dst_end)) - ? tmp_data_1.y : data_1.y; - - data_2.xy = ((dst_index + 6 >= dst_start) && (dst_index + 6 < dst_end)) - ? tmp_data_2.xy : data_2.xy; - - *((__global ushort2 *)((__global char *)dst + dst_index + 0))= data_0; - *((__global ushort2 *)((__global char *)dst + dst_index + 4))= data_1; - *((__global ushort2 *)((__global char *)dst + dst_index + 8))= data_2; - } -} -__kernel void arithm_s_absdiff_C3_D3 (__global short *src1, int src1_step, int src1_offset, - __global short *dst, int dst_step, int dst_offset, - int4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - x = x << 1; - -#ifdef dst_align -#undef dst_align -#endif -#define dst_align (((dst_offset % dst_step) / 6 ) & 1) - int src1_index = mad24(y, src1_step, (x * 6) + src1_offset - (dst_align * 6)); - - int dst_start = mad24(y, dst_step, dst_offset); - int dst_end = mad24(y, dst_step, dst_offset + dst_step1); - int dst_index = mad24(y, dst_step, dst_offset + (x * 6) - (dst_align * 6)); - - short2 src1_data_0 = vload2(0, (__global short *)((__global char *)src1 + src1_index + 0)); - short2 src1_data_1 = vload2(0, (__global short *)((__global char *)src1 + src1_index + 4)); - short2 src1_data_2 = vload2(0, (__global short *)((__global char *)src1 + src1_index + 8)); - - int2 src2_data_0 = (int2)(src2.x, src2.y); - int2 src2_data_1 = (int2)(src2.z, src2.x); - int2 src2_data_2 = (int2)(src2.y, src2.z); - - short2 data_0 = *((__global short2 *)((__global char *)dst + dst_index + 0)); - short2 data_1 = *((__global short2 *)((__global char *)dst + dst_index + 4)); - short2 data_2 = *((__global short2 *)((__global char *)dst + dst_index + 8)); - - short2 tmp_data_0 = convert_short2_sat(abs_diff(convert_int2_sat(src1_data_0), src2_data_0)); - short2 tmp_data_1 = convert_short2_sat(abs_diff(convert_int2_sat(src1_data_1), src2_data_1)); - short2 tmp_data_2 = convert_short2_sat(abs_diff(convert_int2_sat(src1_data_2), src2_data_2)); - - data_0.xy = ((dst_index + 0 >= dst_start)) ? tmp_data_0.xy : data_0.xy; - - data_1.x = ((dst_index + 0 >= dst_start) && (dst_index + 0 < dst_end)) - ? tmp_data_1.x : data_1.x; - data_1.y = ((dst_index + 6 >= dst_start) && (dst_index + 6 < dst_end)) - ? tmp_data_1.y : data_1.y; - - data_2.xy = ((dst_index + 6 >= dst_start) && (dst_index + 6 < dst_end)) - ? tmp_data_2.xy : data_2.xy; - - *((__global short2 *)((__global char *)dst + dst_index + 0))= data_0; - *((__global short2 *)((__global char *)dst + dst_index + 4))= data_1; - *((__global short2 *)((__global char *)dst + dst_index + 8))= data_2; - } -} -__kernel void arithm_s_absdiff_C3_D4 (__global int *src1, int src1_step, int src1_offset, - __global int *dst, int dst_step, int dst_offset, - int4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x * 12) + src1_offset); - int dst_index = mad24(y, dst_step, dst_offset + (x * 12)); - - int src1_data_0 = *((__global int *)((__global char *)src1 + src1_index + 0)); - int src1_data_1 = *((__global int *)((__global char *)src1 + src1_index + 4)); - int src1_data_2 = *((__global int *)((__global char *)src1 + src1_index + 8)); - - int src2_data_0 = src2.x; - int src2_data_1 = src2.y; - int src2_data_2 = src2.z; - - int data_0 = *((__global int *)((__global char *)dst + dst_index + 0)); - int data_1 = *((__global int *)((__global char *)dst + dst_index + 4)); - int data_2 = *((__global int *)((__global char *)dst + dst_index + 8)); - - int tmp_data_0 = convert_int_sat(abs_diff(src1_data_0, src2_data_0)); - int tmp_data_1 = convert_int_sat(abs_diff(src1_data_1, src2_data_1)); - int tmp_data_2 = convert_int_sat(abs_diff(src1_data_2, src2_data_2)); - - *((__global int *)((__global char *)dst + dst_index + 0))= tmp_data_0; - *((__global int *)((__global char *)dst + dst_index + 4))= tmp_data_1; - *((__global int *)((__global char *)dst + dst_index + 8))= tmp_data_2; - } -} -__kernel void arithm_s_absdiff_C3_D5 (__global float *src1, int src1_step, int src1_offset, - __global float *dst, int dst_step, int dst_offset, - float4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x * 12) + src1_offset); - int dst_index = mad24(y, dst_step, dst_offset + (x * 12)); - - float src1_data_0 = *((__global float *)((__global char *)src1 + src1_index + 0)); - float src1_data_1 = *((__global float *)((__global char *)src1 + src1_index + 4)); - float src1_data_2 = *((__global float *)((__global char *)src1 + src1_index + 8)); - - float src2_data_0 = src2.x; - float src2_data_1 = src2.y; - float src2_data_2 = src2.z; - - float data_0 = *((__global float *)((__global char *)dst + dst_index + 0)); - float data_1 = *((__global float *)((__global char *)dst + dst_index + 4)); - float data_2 = *((__global float *)((__global char *)dst + dst_index + 8)); - - float tmp_data_0 = fabs(src1_data_0 - src2_data_0); - float tmp_data_1 = fabs(src1_data_1 - src2_data_1); - float tmp_data_2 = fabs(src1_data_2 - src2_data_2); - - *((__global float *)((__global char *)dst + dst_index + 0))= tmp_data_0; - *((__global float *)((__global char *)dst + dst_index + 4))= tmp_data_1; - *((__global float *)((__global char *)dst + dst_index + 8))= tmp_data_2; - } -} - -#if defined (DOUBLE_SUPPORT) -__kernel void arithm_s_absdiff_C3_D6 (__global double *src1, int src1_step, int src1_offset, - __global double *dst, int dst_step, int dst_offset, - double4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x * 24) + src1_offset); - int dst_index = mad24(y, dst_step, dst_offset + (x * 24)); - - double src1_data_0 = *((__global double *)((__global char *)src1 + src1_index + 0 )); - double src1_data_1 = *((__global double *)((__global char *)src1 + src1_index + 8 )); - double src1_data_2 = *((__global double *)((__global char *)src1 + src1_index + 16)); - - double src2_data_0 = src2.x; - double src2_data_1 = src2.y; - double src2_data_2 = src2.z; - - double data_0 = *((__global double *)((__global char *)dst + dst_index + 0 )); - double data_1 = *((__global double *)((__global char *)dst + dst_index + 8 )); - double data_2 = *((__global double *)((__global char *)dst + dst_index + 16)); - - double tmp_data_0 = fabs(src1_data_0 - src2_data_0); - double tmp_data_1 = fabs(src1_data_1 - src2_data_1); - double tmp_data_2 = fabs(src1_data_2 - src2_data_2); - - *((__global double *)((__global char *)dst + dst_index + 0 ))= tmp_data_0; - *((__global double *)((__global char *)dst + dst_index + 8 ))= tmp_data_1; - *((__global double *)((__global char *)dst + dst_index + 16))= tmp_data_2; - } -} -#endif -__kernel void arithm_s_absdiff_C4_D0 (__global uchar *src1, int src1_step, int src1_offset, - __global uchar *dst, int dst_step, int dst_offset, - int4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 2) + src1_offset); - int dst_index = mad24(y, dst_step, (x << 2) + dst_offset); - - uchar4 src_data1 = *((__global uchar4 *)(src1 + src1_index)); - - uchar4 data = convert_uchar4_sat(abs_diff(convert_int4_sat(src_data1), src2)); - - *((__global uchar4 *)(dst + dst_index)) = data; - } -} -__kernel void arithm_s_absdiff_C4_D2 (__global ushort *src1, int src1_step, int src1_offset, - __global ushort *dst, int dst_step, int dst_offset, - int4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 3) + src1_offset); - int dst_index = mad24(y, dst_step, (x << 3) + dst_offset); - - ushort4 src_data1 = *((__global ushort4 *)((__global char *)src1 + src1_index)); - - ushort4 data = convert_ushort4_sat(abs_diff(convert_int4_sat(src_data1), src2)); - - *((__global ushort4 *)((__global char *)dst + dst_index)) = data; - } -} -__kernel void arithm_s_absdiff_C4_D3 (__global short *src1, int src1_step, int src1_offset, - __global short *dst, int dst_step, int dst_offset, - int4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 3) + src1_offset); - int dst_index = mad24(y, dst_step, (x << 3) + dst_offset); - - short4 src_data1 = *((__global short4 *)((__global char *)src1 + src1_index)); - - short4 data = convert_short4_sat(abs_diff(convert_int4_sat(src_data1), src2)); - - *((__global short4 *)((__global char *)dst + dst_index)) = data; - } -} -__kernel void arithm_s_absdiff_C4_D4 (__global int *src1, int src1_step, int src1_offset, - __global int *dst, int dst_step, int dst_offset, - int4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 4) + src1_offset); - int dst_index = mad24(y, dst_step, (x << 4) + dst_offset); - - int4 src_data1 = *((__global int4 *)((__global char *)src1 + src1_index)); - - int4 data = convert_int4_sat(abs_diff(src_data1, src2)); - - *((__global int4 *)((__global char *)dst + dst_index)) = data; - } -} -__kernel void arithm_s_absdiff_C4_D5 (__global float *src1, int src1_step, int src1_offset, - __global float *dst, int dst_step, int dst_offset, - float4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 4) + src1_offset); - int dst_index = mad24(y, dst_step, (x << 4) + dst_offset); - - float4 src_data1 = *((__global float4 *)((__global char *)src1 + src1_index)); - - float4 data = fabs(src_data1 - src2); - - *((__global float4 *)((__global char *)dst + dst_index)) = data; - } -} - -#if defined (DOUBLE_SUPPORT) -__kernel void arithm_s_absdiff_C4_D6 (__global double *src1, int src1_step, int src1_offset, - __global double *dst, int dst_step, int dst_offset, - double4 src2, int rows, int cols, int dst_step1) -{ - - int x = get_global_id(0); - int y = get_global_id(1); - - if (x < cols && y < rows) - { - int src1_index = mad24(y, src1_step, (x << 5) + src1_offset); - int dst_index = mad24(y, dst_step, (x << 5) + dst_offset); - - double4 src_data1 = *((__global double4 *)((__global char *)src1 + src1_index)); - - double4 data = fabs(src_data1 - src2); - - *((__global double4 *)((__global char *)dst + dst_index)) = data; - } -} -#endif From ce557fb7befd61f721f7c7ff58a41b39fc21b722 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Tue, 24 Sep 2013 15:12:24 +0400 Subject: [PATCH 50/68] Revert "cmake: fixed incorrect usage of add_definitions() on Linux" This reverts commit 286244efedd215600b6be72d4c25e48af0ed1c2d. --- cmake/OpenCVModule.cmake | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cmake/OpenCVModule.cmake b/cmake/OpenCVModule.cmake index 1d87bc1b88..4076d9fab7 100644 --- a/cmake/OpenCVModule.cmake +++ b/cmake/OpenCVModule.cmake @@ -519,7 +519,11 @@ macro(ocv_create_module) if((NOT DEFINED OPENCV_MODULE_TYPE AND BUILD_SHARED_LIBS) OR (DEFINED OPENCV_MODULE_TYPE AND OPENCV_MODULE_TYPE STREQUAL SHARED)) - set_target_properties(${the_module} PROPERTIES DEFINE_SYMBOL CVAPI_EXPORTS) + if(MSVC) + set_target_properties(${the_module} PROPERTIES DEFINE_SYMBOL CVAPI_EXPORTS) + else() + add_definitions(-DCVAPI_EXPORTS) + endif() endif() if(MSVC) From 192ca711efd8efb4ea6e9e3b439e518e8caff9ad Mon Sep 17 00:00:00 2001 From: George Andrew Brindeiro Date: Tue, 24 Sep 2013 12:59:37 -0300 Subject: [PATCH 51/68] Added nonfree headers that are required for using SURF features --- doc/tutorials/features2d/feature_detection/feature_detection.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/tutorials/features2d/feature_detection/feature_detection.rst b/doc/tutorials/features2d/feature_detection/feature_detection.rst index 0e690404a8..51776de1fc 100644 --- a/doc/tutorials/features2d/feature_detection/feature_detection.rst +++ b/doc/tutorials/features2d/feature_detection/feature_detection.rst @@ -30,6 +30,7 @@ This tutorial code's is shown lines below. You can also download it from `here < #include #include "opencv2/core/core.hpp" #include "opencv2/features2d/features2d.hpp" + #include "opencv2/nonfree/features2d.hpp" #include "opencv2/highgui/highgui.hpp" #include "opencv2/nonfree/nonfree.hpp" From 48774a39b400a256379f3caa55bc0483f717ee6d Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Sat, 21 Sep 2013 21:12:39 +0400 Subject: [PATCH 52/68] cmake: fixed incorrect usage of add_definitions() on Linux --- cmake/OpenCVModule.cmake | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/cmake/OpenCVModule.cmake b/cmake/OpenCVModule.cmake index 4076d9fab7..1d87bc1b88 100644 --- a/cmake/OpenCVModule.cmake +++ b/cmake/OpenCVModule.cmake @@ -519,11 +519,7 @@ macro(ocv_create_module) if((NOT DEFINED OPENCV_MODULE_TYPE AND BUILD_SHARED_LIBS) OR (DEFINED OPENCV_MODULE_TYPE AND OPENCV_MODULE_TYPE STREQUAL SHARED)) - if(MSVC) - set_target_properties(${the_module} PROPERTIES DEFINE_SYMBOL CVAPI_EXPORTS) - else() - add_definitions(-DCVAPI_EXPORTS) - endif() + set_target_properties(${the_module} PROPERTIES DEFINE_SYMBOL CVAPI_EXPORTS) endif() if(MSVC) From b4a534a23568e97be58fe64ed97dd9e694af4321 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Tue, 24 Sep 2013 20:17:39 +0400 Subject: [PATCH 53/68] cmake: PCH: use DEFINE_SYMBOL target property --- cmake/OpenCVPCHSupport.cmake | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/cmake/OpenCVPCHSupport.cmake b/cmake/OpenCVPCHSupport.cmake index 9b849ebd79..328386a4dd 100644 --- a/cmake/OpenCVPCHSupport.cmake +++ b/cmake/OpenCVPCHSupport.cmake @@ -25,11 +25,13 @@ IF(CMAKE_COMPILER_IS_GNUCXX) SET(_PCH_include_prefix "-I") SET(_PCH_isystem_prefix "-isystem") + SET(_PCH_define_prefix "-D") ELSEIF(CMAKE_GENERATOR MATCHES "^Visual.*$") SET(PCHSupport_FOUND TRUE) SET(_PCH_include_prefix "/I") SET(_PCH_isystem_prefix "/I") + SET(_PCH_define_prefix "/D") ELSE() SET(PCHSupport_FOUND FALSE) ENDIF() @@ -237,6 +239,14 @@ MACRO(ADD_PRECOMPILED_HEADER _targetName _input) _PCH_GET_COMPILE_FLAGS(_compile_FLAGS) + get_target_property(type ${_targetName} TYPE) + if(type STREQUAL "SHARED_LIBRARY") + get_target_property(__DEFINES ${_targetName} DEFINE_SYMBOL) + if(NOT __DEFINES MATCHES __DEFINES-NOTFOUND) + list(APPEND _compile_FLAGS "${_PCH_define_prefix}${__DEFINES}") + endif() + endif() + #MESSAGE("_compile_FLAGS: ${_compile_FLAGS}") #message("COMMAND ${CMAKE_CXX_COMPILER} ${_compile_FLAGS} -x c++-header -o ${_output} ${_input}") From c7c0a41e1709acdf7e3a9303b2f6e18727c7fa3a Mon Sep 17 00:00:00 2001 From: Jin Ma Date: Wed, 25 Sep 2013 17:42:56 +0800 Subject: [PATCH 54/68] Resolved the bug that the work group size is not valid. --- modules/ocl/src/stereo_csbp.cpp | 40 +++++++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/modules/ocl/src/stereo_csbp.cpp b/modules/ocl/src/stereo_csbp.cpp index b119eadf96..9052dc82bd 100644 --- a/modules/ocl/src/stereo_csbp.cpp +++ b/modules/ocl/src/stereo_csbp.cpp @@ -96,6 +96,10 @@ namespace cv { namespace stereoCSBP { + static inline int divUp(int total, int grain) + { + return (total + grain - 1) / grain; + } static string get_kernel_name(string kernel_name, int data_type) { stringstream idxStr; @@ -125,7 +129,10 @@ namespace cv //size_t blockSize = 256; size_t localThreads[] = {32, 8 ,1}; - size_t globalThreads[] = { w, h, 1 }; + size_t globalThreads[] = {divUp(w, localThreads[0]) *localThreads[0], + divUp(h, localThreads[1]) *localThreads[1], + 1 + }; int cdisp_step1 = msg_step * h; openCLVerifyKernel(clCxt, kernel, localThreads); @@ -212,7 +219,10 @@ namespace cv //size_t blockSize = 256; size_t localThreads[] = {32, 8 ,1}; - size_t globalThreads[] = { w, h, 1 }; + size_t globalThreads[] = {divUp(w, localThreads[0]) *localThreads[0], + divUp(h, localThreads[1]) *localThreads[1], + 1 + }; int disp_step = msg_step * h; openCLVerifyKernel(clCxt, kernel, localThreads); @@ -244,7 +254,10 @@ namespace cv //size_t blockSize = 256; size_t localThreads[] = {32, 8, 1}; - size_t globalThreads[] = { w, h, 1 }; + size_t globalThreads[] = {divUp(w, localThreads[0]) *localThreads[0], + divUp(h, localThreads[1]) *localThreads[1], + 1 + }; int disp_step = msg_step * h; openCLVerifyKernel(clCxt, kernel, localThreads); @@ -275,10 +288,14 @@ namespace cv init_data_cost_reduce_caller(left, right, temp, rthis, msg_step, h, w, level); if(rthis.use_local_init_data_cost == true) + { get_first_initial_local_caller(data_cost_selected, disp_selected_pyr, temp, rthis, h, w, nr_plane, msg_step); + } else + { get_first_initial_global_caller(data_cost_selected, disp_selected_pyr, temp, rthis, h, w, nr_plane, msg_step); + } } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -297,8 +314,11 @@ namespace cv cl_kernel kernel = openCLGetKernelFromSource(clCxt, &stereocsbp, kernelName); - size_t localThreads[] = { 32, 8, 1 }; - size_t globalThreads[] = { w, h, 1 }; + size_t localThreads[] = {32, 8, 1}; + size_t globalThreads[] = {divUp(w, localThreads[0]) *localThreads[0], + divUp(h, localThreads[1]) *localThreads[1], + 1 + }; int disp_step1 = msg_step1 * h; int disp_step2 = msg_step2 * h2; @@ -407,7 +427,10 @@ namespace cv //size_t blockSize = 256; size_t localThreads[] = {32, 8, 1}; - size_t globalThreads[] = { w, h, 1 }; + size_t globalThreads[] = {divUp(w, localThreads[0]) *localThreads[0], + divUp(h, localThreads[1]) *localThreads[1], + 1 + }; int disp_step1 = msg_step1 * h; int disp_step2 = msg_step2 * h2; @@ -508,7 +531,10 @@ namespace cv //size_t blockSize = 256; size_t localThreads[] = {32, 8, 1}; - size_t globalThreads[] = { disp.cols, disp.rows, 1 }; + size_t globalThreads[] = {divUp(disp.cols, localThreads[0]) *localThreads[0], + divUp(disp.rows, localThreads[1]) *localThreads[1], + 1 + }; int step_size = disp.step / disp.elemSize(); int disp_step = disp.rows * msg_step; From 544c02407e29cbe3da5cd68073d359395f1448b1 Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Tue, 24 Sep 2013 22:54:13 +0400 Subject: [PATCH 55/68] reverted and generalized original ocl::transpose --- modules/ocl/src/arithm.cpp | 12 ++-- modules/ocl/src/opencl/arithm_minMax.cl | 5 ++ modules/ocl/src/opencl/arithm_transpose.cl | 67 ++++++++++++++++++++-- modules/ocl/test/utility.hpp | 2 +- 4 files changed, 72 insertions(+), 14 deletions(-) diff --git a/modules/ocl/src/arithm.cpp b/modules/ocl/src/arithm.cpp index 035cea781e..c1147cb413 100644 --- a/modules/ocl/src/arithm.cpp +++ b/modules/ocl/src/arithm.cpp @@ -64,7 +64,6 @@ namespace cv { //////////////////////////////// OpenCL kernel strings ///////////////////// - extern const char *transpose_kernel; extern const char *arithm_nonzero; extern const char *arithm_sum; extern const char *arithm_sum_3; @@ -1265,9 +1264,8 @@ int cv::ocl::countNonZero(const oclMat &src) CV_Error(CV_GpuNotSupported, "select device don't support double"); } CV_Assert(groupnum != 0); - groupnum = groupnum * 2; +// groupnum = groupnum * 2; int vlen = 8 , dbsize = groupnum * vlen; - //cl_ulong start, end; Context *clCxt = src.clCxt; string kernelName = "arithm_op_nonzero"; int *p = new int[dbsize], nonzero = 0; @@ -1529,7 +1527,7 @@ oclMatExpr::operator oclMat() const #define TILE_DIM (32) #define BLOCK_ROWS (256/TILE_DIM) -static void transpose_run(const oclMat &src, oclMat &dst, string kernelName) +static void transpose_run(const oclMat &src, oclMat &dst, string kernelName, bool inplace = false) { Context *clCxt = src.clCxt; if (!clCxt->supportsFeature(Context::CL_DOUBLE) && src.depth() == CV_64F) @@ -1544,7 +1542,7 @@ static void transpose_run(const oclMat &src, oclMat &dst, string kernelName) channelsString[src.channels()]); size_t localThreads[3] = { TILE_DIM, BLOCK_ROWS, 1 }; - size_t globalThreads[3] = { src.cols, src.rows, 1 }; + size_t globalThreads[3] = { src.cols, inplace ? src.rows : divUp(src.rows, TILE_DIM) * BLOCK_ROWS, 1 }; int srcstep1 = src.step / src.elemSize(), dststep1 = dst.step / dst.elemSize(); int srcoffset1 = src.offset / src.elemSize(), dstoffset1 = dst.offset / dst.elemSize(); @@ -1568,8 +1566,8 @@ void cv::ocl::transpose(const oclMat &src, oclMat &dst) CV_Assert(src.depth() <= CV_64F && src.channels() <= 4); if ( src.data == dst.data && src.cols == src.rows && dst.offset == src.offset - && dst.rows == dst.cols && src.cols == dst.cols) - transpose_run( src, dst, "transpose_inplace"); + && dst.size() == src.size()) + transpose_run( src, dst, "transpose_inplace", true); else { dst.create(src.cols, src.rows, src.type()); diff --git a/modules/ocl/src/opencl/arithm_minMax.cl b/modules/ocl/src/opencl/arithm_minMax.cl index 1dcb138ebd..23b2933066 100644 --- a/modules/ocl/src/opencl/arithm_minMax.cl +++ b/modules/ocl/src/opencl/arithm_minMax.cl @@ -44,9 +44,14 @@ //M*/ /**************************************PUBLICFUNC*************************************/ + #if defined (DOUBLE_SUPPORT) +#ifdef cl_amd_fp64 +#pragma OPENCL EXTENSION cl_amd_fp64:enable +#elif defined (cl_khr_fp64) #pragma OPENCL EXTENSION cl_khr_fp64:enable #endif +#endif #if defined (DEPTH_0) #define VEC_TYPE uchar8 diff --git a/modules/ocl/src/opencl/arithm_transpose.cl b/modules/ocl/src/opencl/arithm_transpose.cl index 57f7f1b9d3..5328d1f1b2 100644 --- a/modules/ocl/src/opencl/arithm_transpose.cl +++ b/modules/ocl/src/opencl/arithm_transpose.cl @@ -44,23 +44,78 @@ //M*/ #if defined (DOUBLE_SUPPORT) +#ifdef cl_amd_fp64 +#pragma OPENCL EXTENSION cl_amd_fp64:enable +#elif defined (cl_khr_fp64) #pragma OPENCL EXTENSION cl_khr_fp64:enable #endif +#endif + +#define TILE_DIM 32 +#define BLOCK_ROWS 8 +#define LDS_STEP TILE_DIM __kernel void transpose(__global const T* src, __global T* dst, int src_cols, int src_rows, int src_step, int dst_step, int src_offset, int dst_offset) { - int x = get_global_id(0); - int y = get_global_id(1); + int gp_x = get_group_id(0), gp_y = get_group_id(1); + int gs_x = get_num_groups(0), gs_y = get_num_groups(1); + + int groupId_x, groupId_y; + + if(src_rows == src_cols) + { + groupId_y = gp_x; + groupId_x = (gp_x + gp_y) % gs_x; + } + else + { + int bid = gp_x + gs_x * gp_y; + groupId_y = bid % gs_y; + groupId_x = ((bid / gs_y) + groupId_y) % gs_x; + } + + int lx = get_local_id(0); + int ly = get_local_id(1); + + int x = groupId_x * TILE_DIM + lx; + int y = groupId_y * TILE_DIM + ly; + + int x_index = groupId_y * TILE_DIM + lx; + int y_index = groupId_x * TILE_DIM + ly; + + __local T title[TILE_DIM * LDS_STEP]; if (x < src_cols && y < src_rows) { - int srcIdx = mad24(y, src_step, src_offset + x); - int dstIdx = mad24(x, dst_step, dst_offset + y); + int index_src = mad24(y, src_step, x); - dst[dstIdx] = src[srcIdx]; + for(int i = 0; i < TILE_DIM; i += BLOCK_ROWS) + { + if (y + i < src_rows) + { + title[(ly + i) * LDS_STEP + lx] = src[src_offset + index_src]; + index_src = mad24(BLOCK_ROWS, src_step, index_src); + } + } + } + + barrier(CLK_LOCAL_MEM_FENCE); + + if (x_index < src_rows && y_index < src_cols) + { + int index_dst = mad24(y_index, dst_step, x_index); + + for(int i = 0; i < TILE_DIM; i += BLOCK_ROWS) + { + if ((y_index + i) < src_cols) + { + dst[dst_offset + index_dst] = title[lx * LDS_STEP + ly + i]; + index_dst += dst_step * BLOCK_ROWS; + } + } } } @@ -72,7 +127,7 @@ __kernel void transpose_inplace(__global T* src, __global T* dst, int x = get_global_id(0); int y = get_global_id(1); - if (x < src_cols && y < src_rows && x < y) + if (y < src_rows && x < y) { int srcIdx = mad24(y, src_step, src_offset + x); int dstIdx = mad24(x, dst_step, dst_offset + y); diff --git a/modules/ocl/test/utility.hpp b/modules/ocl/test/utility.hpp index 48c8bbcd9b..7c491916fc 100644 --- a/modules/ocl/test/utility.hpp +++ b/modules/ocl/test/utility.hpp @@ -48,7 +48,7 @@ #define MHEIGHT 256 #define MIN_VALUE 171 -#define MAX_VALUE 351 +#define MAX_VALUE 357 //#define RANDOMROI int randomInt(int minVal, int maxVal); From 0faac595a849c1868ed70a75e641b2663151c8eb Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Wed, 25 Sep 2013 15:02:47 +0400 Subject: [PATCH 56/68] fixed minMaxLoc kernel (removed compilation errors) --- modules/ocl/src/arithm.cpp | 126 +++++++------ modules/ocl/src/opencl/arithm_minMaxLoc.cl | 206 ++++----------------- 2 files changed, 102 insertions(+), 230 deletions(-) diff --git a/modules/ocl/src/arithm.cpp b/modules/ocl/src/arithm.cpp index c1147cb413..8d502ea56c 100644 --- a/modules/ocl/src/arithm.cpp +++ b/modules/ocl/src/arithm.cpp @@ -341,7 +341,7 @@ static void arithmetic_sum_buffer_run(const oclMat &src, cl_mem &dst, int vlen , args.push_back( make_pair( sizeof(cl_mem) , (void *)&src.data)); args.push_back( make_pair( sizeof(cl_mem) , (void *)&dst )); size_t gt[3] = {groupnum * 256, 1, 1}, lt[3] = {256, 1, 1}; - if(src.oclchannels() != 3) + if (src.oclchannels() != 3) openCLExecuteKernel(src.clCxt, &arithm_sum, "arithm_op_sum", gt, lt, args, -1, -1, build_options); else openCLExecuteKernel(src.clCxt, &arithm_sum_3, "arithm_op_sum_3", gt, lt, args, -1, -1, build_options); @@ -365,9 +365,9 @@ Scalar arithmetic_sum(const oclMat &src, int type = 0) memset(p, 0, dbsize * sizeof(T)); openCLReadBuffer(clCxt, dstBuffer, (void *)p, dbsize * sizeof(T)); - for(int i = 0; i < dbsize;) + for (int i = 0; i < dbsize;) { - for(int j = 0; j < src.oclchannels(); j++, i++) + for (int j = 0; j < src.oclchannels(); j++, i++) s.val[j] += p[i]; } delete[] p; @@ -378,9 +378,9 @@ Scalar arithmetic_sum(const oclMat &src, int type = 0) typedef Scalar (*sumFunc)(const oclMat &src, int type); Scalar cv::ocl::sum(const oclMat &src) { - if(!src.clCxt->supportsFeature(Context::CL_DOUBLE) && src.depth() == CV_64F) + if (!src.clCxt->supportsFeature(Context::CL_DOUBLE) && src.depth() == CV_64F) { - CV_Error(CV_GpuNotSupported, "select device don't support double"); + CV_Error(CV_GpuNotSupported, "Selected device doesn't support double"); } static sumFunc functab[2] = { @@ -395,9 +395,9 @@ Scalar cv::ocl::sum(const oclMat &src) Scalar cv::ocl::absSum(const oclMat &src) { - if(!src.clCxt->supportsFeature(Context::CL_DOUBLE) && src.depth() == CV_64F) + if (!src.clCxt->supportsFeature(Context::CL_DOUBLE) && src.depth() == CV_64F) { - CV_Error(CV_GpuNotSupported, "select device don't support double"); + CV_Error(CV_GpuNotSupported, "Selected device doesn't support double"); } static sumFunc functab[2] = { @@ -412,9 +412,9 @@ Scalar cv::ocl::absSum(const oclMat &src) Scalar cv::ocl::sqrSum(const oclMat &src) { - if(!src.clCxt->supportsFeature(Context::CL_DOUBLE) && src.depth() == CV_64F) + if (!src.clCxt->supportsFeature(Context::CL_DOUBLE) && src.depth() == CV_64F) { - CV_Error(CV_GpuNotSupported, "select device don't support double"); + CV_Error(CV_GpuNotSupported, "Selected device doesn't support double"); } static sumFunc functab[2] = { @@ -446,7 +446,7 @@ void cv::ocl::meanStdDev(const oclMat &src, Scalar &mean, Scalar &stddev) m1 = (Mat)dst1; m2 = (Mat)dst2; int i = 0, *p = (int *)m1.data, *q = (int *)m2.data; - for(; i < channels; i++) + for (; i < channels; i++) { mean.val[i] = (double)p[i] / (src.cols * src.rows); stddev.val[i] = std::sqrt(std::max((double) q[i] / (src.cols * src.rows) - mean.val[i] * mean.val[i] , 0.)); @@ -476,7 +476,7 @@ static void arithmetic_minMax_run(const oclMat &src, const oclMat &mask, cl_mem args.push_back( make_pair( sizeof(cl_int) , (void *)&elemnum)); args.push_back( make_pair( sizeof(cl_int) , (void *)&groupnum)); args.push_back( make_pair( sizeof(cl_mem) , (void *)&src.data)); - if(!mask.empty()) + if (!mask.empty()) { int mall_cols = mask.step / (vlen * mask.elemSize1()); int mpre_cols = (mask.offset % mask.step) / (vlen * mask.elemSize1()); @@ -499,7 +499,7 @@ static void arithmetic_minMax_mask_run(const oclMat &src, const oclMat &mask, cl vector > args; size_t gt[3] = {groupnum * 256, 1, 1}, lt[3] = {256, 1, 1}; char build_options[50]; - if(src.oclchannels() == 1) + if (src.oclchannels() == 1) { int cols = (src.cols - 1) / vlen + 1; int invalid_cols = src.step / (vlen * src.elemSize1()) - cols; @@ -519,8 +519,6 @@ static void arithmetic_minMax_mask_run(const oclMat &src, const oclMat &mask, cl args.push_back( make_pair( sizeof(cl_int) , (void *)&moffset )); args.push_back( make_pair( sizeof(cl_mem) , (void *)&mask.data )); args.push_back( make_pair( sizeof(cl_mem) , (void *)&dst )); - // printf("elemnum:%d,cols:%d,invalid_cols:%d,offset:%d,minvalid_cols:%d,moffset:%d,repeat_e:%d\r\n", - // elemnum,cols,invalid_cols,offset,minvalid_cols,moffset,repeat_me); openCLExecuteKernel(src.clCxt, &arithm_minMax_mask, kernelName, gt, lt, args, -1, -1, build_options); } } @@ -549,18 +547,18 @@ template void arithmetic_minMax(const oclMat &src, double *minVal, Mat matbuf = Mat(buf); T *p = matbuf.ptr(); - if(minVal != NULL) + if (minVal != NULL) { *minVal = std::numeric_limits::max(); - for(int i = 0; i < vlen * (int)groupnum; i++) + for (int i = 0; i < vlen * (int)groupnum; i++) { *minVal = *minVal < p[i] ? *minVal : p[i]; } } - if(maxVal != NULL) + if (maxVal != NULL) { *maxVal = -std::numeric_limits::max(); - for(int i = vlen * (int)groupnum; i < 2 * vlen * (int)groupnum; i++) + for (int i = vlen * (int)groupnum; i < 2 * vlen * (int)groupnum; i++) { *maxVal = *maxVal > p[i] ? *maxVal : p[i]; } @@ -577,9 +575,9 @@ void cv::ocl::minMax(const oclMat &src, double *minVal, double *maxVal, const oc void cv::ocl::minMax_buf(const oclMat &src, double *minVal, double *maxVal, const oclMat &mask, oclMat &buf) { CV_Assert(src.oclchannels() == 1); - if(!src.clCxt->supportsFeature(Context::CL_DOUBLE) && src.depth() == CV_64F) + if (!src.clCxt->supportsFeature(Context::CL_DOUBLE) && src.depth() == CV_64F) { - CV_Error(CV_GpuNotSupported, "select device don't support double"); + CV_Error(CV_GpuNotSupported, "Selected device doesn't support double"); } static minMaxFunc functab[8] = { @@ -625,7 +623,7 @@ double cv::ocl::norm(const oclMat &src1, const oclMat &src2, int normType) m = (gm2); p = (int *)m.data; r = -std::numeric_limits::max(); - for(i = 0; i < channels; i++) + for (i = 0; i < channels; i++) { r = std::max(r, (double)p[i]); } @@ -635,7 +633,7 @@ double cv::ocl::norm(const oclMat &src1, const oclMat &src2, int normType) //arithmetic_sum_run(gm1, gm2,"arithm_op_sum"); m = (gm2); p = (int *)m.data; - for(i = 0; i < channels; i++) + for (i = 0; i < channels; i++) { r = r + (double)p[i]; } @@ -645,14 +643,14 @@ double cv::ocl::norm(const oclMat &src1, const oclMat &src2, int normType) //arithmetic_sum_run(gm1, gm2,"arithm_op_squares_sum"); m = (gm2); p = (int *)m.data; - for(i = 0; i < channels; i++) + for (i = 0; i < channels; i++) { r = r + (double)p[i]; } r = std::sqrt(r); break; } - if(isRelative) + if (isRelative) r = r / norm(src2, normType); return r; } @@ -663,9 +661,9 @@ double cv::ocl::norm(const oclMat &src1, const oclMat &src2, int normType) static void arithmetic_flip_rows_run(const oclMat &src, oclMat &dst, string kernelName) { - if(!src.clCxt->supportsFeature(Context::CL_DOUBLE) && src.type() == CV_64F) + if (!src.clCxt->supportsFeature(Context::CL_DOUBLE) && src.type() == CV_64F) { - CV_Error(CV_GpuNotSupported, "Selected device don't support double\r\n"); + CV_Error(CV_GpuNotSupported, "Selected device doesn't support double\r\n"); return; } @@ -710,9 +708,9 @@ static void arithmetic_flip_rows_run(const oclMat &src, oclMat &dst, string kern static void arithmetic_flip_cols_run(const oclMat &src, oclMat &dst, string kernelName, bool isVertical) { - if(!src.clCxt->supportsFeature(Context::CL_DOUBLE) && src.type() == CV_64F) + if (!src.clCxt->supportsFeature(Context::CL_DOUBLE) && src.type() == CV_64F) { - CV_Error(CV_GpuNotSupported, "Selected device don't support double\r\n"); + CV_Error(CV_GpuNotSupported, "Selected device doesn't support double\r\n"); return; } @@ -749,7 +747,7 @@ static void arithmetic_flip_cols_run(const oclMat &src, oclMat &dst, string kern args.push_back( make_pair( sizeof(cl_int), (void *)&dst.rows )); args.push_back( make_pair( sizeof(cl_int), (void *)&dst.cols )); - if(isVertical) + if (isVertical) args.push_back( make_pair( sizeof(cl_int), (void *)&rows )); else args.push_back( make_pair( sizeof(cl_int), (void *)&cols )); @@ -764,11 +762,11 @@ static void arithmetic_flip_cols_run(const oclMat &src, oclMat &dst, string kern void cv::ocl::flip(const oclMat &src, oclMat &dst, int flipCode) { dst.create(src.size(), src.type()); - if(flipCode == 0) + if (flipCode == 0) { arithmetic_flip_rows_run(src, dst, "arithm_flip_rows"); } - else if(flipCode > 0) + else if (flipCode > 0) arithmetic_flip_cols_run(src, dst, "arithm_flip_cols", false); else arithmetic_flip_cols_run(src, dst, "arithm_flip_rc", true); @@ -877,9 +875,9 @@ void cv::ocl::log(const oclMat &src, oclMat &dst) static void arithmetic_magnitude_phase_run(const oclMat &src1, const oclMat &src2, oclMat &dst, string kernelName) { - if(!src1.clCxt->supportsFeature(Context::CL_DOUBLE) && src1.type() == CV_64F) + if (!src1.clCxt->supportsFeature(Context::CL_DOUBLE) && src1.type() == CV_64F) { - CV_Error(CV_GpuNotSupported, "Selected device don't support double\r\n"); + CV_Error(CV_GpuNotSupported, "Selected device doesn't support double\r\n"); return; } @@ -921,9 +919,9 @@ void cv::ocl::magnitude(const oclMat &src1, const oclMat &src2, oclMat &dst) static void arithmetic_phase_run(const oclMat &src1, const oclMat &src2, oclMat &dst, string kernelName, const char **kernelString) { - if(!src1.clCxt->supportsFeature(Context::CL_DOUBLE) && src1.type() == CV_64F) + if (!src1.clCxt->supportsFeature(Context::CL_DOUBLE) && src1.type() == CV_64F) { - CV_Error(CV_GpuNotSupported, "Selected device don't support double\r\n"); + CV_Error(CV_GpuNotSupported, "Selected device doesn't support double\r\n"); return; } @@ -964,7 +962,7 @@ void cv::ocl::phase(const oclMat &x, const oclMat &y, oclMat &Angle , bool angle CV_Assert(x.type() == y.type() && x.size() == y.size() && (x.depth() == CV_32F || x.depth() == CV_64F)); Angle.create(x.size(), x.type()); string kernelName = angleInDegrees ? "arithm_phase_indegrees" : "arithm_phase_inradians"; - if(angleInDegrees) + if (angleInDegrees) arithmetic_phase_run(x, y, Angle, kernelName, &arithm_phase); else arithmetic_phase_run(x, y, Angle, kernelName, &arithm_phase); @@ -977,9 +975,9 @@ void cv::ocl::phase(const oclMat &x, const oclMat &y, oclMat &Angle , bool angle static void arithmetic_cartToPolar_run(const oclMat &src1, const oclMat &src2, oclMat &dst_mag, oclMat &dst_cart, string kernelName, bool angleInDegrees) { - if(!src1.clCxt->supportsFeature(Context::CL_DOUBLE) && src1.type() == CV_64F) + if (!src1.clCxt->supportsFeature(Context::CL_DOUBLE) && src1.type() == CV_64F) { - CV_Error(CV_GpuNotSupported, "Selected device don't support double\r\n"); + CV_Error(CV_GpuNotSupported, "Selected device doesn't support double\r\n"); return; } @@ -1030,9 +1028,9 @@ void cv::ocl::cartToPolar(const oclMat &x, const oclMat &y, oclMat &mag, oclMat static void arithmetic_ptc_run(const oclMat &src1, const oclMat &src2, oclMat &dst1, oclMat &dst2, bool angleInDegrees, string kernelName) { - if(!src1.clCxt->supportsFeature(Context::CL_DOUBLE) && src1.type() == CV_64F) + if (!src1.clCxt->supportsFeature(Context::CL_DOUBLE) && src1.type() == CV_64F) { - CV_Error(CV_GpuNotSupported, "Selected device don't support double\r\n"); + CV_Error(CV_GpuNotSupported, "Selected device doesn't support double\r\n"); return; } @@ -1048,7 +1046,7 @@ static void arithmetic_ptc_run(const oclMat &src1, const oclMat &src2, oclMat &d int tmp = angleInDegrees ? 1 : 0; vector > args; - if(src1.data) + if (src1.data) { args.push_back( make_pair( sizeof(cl_mem), (void *)&src1.data )); args.push_back( make_pair( sizeof(cl_int), (void *)&src1.step )); @@ -1077,7 +1075,7 @@ void cv::ocl::polarToCart(const oclMat &magnitude, const oclMat &angle, oclMat & x.create(angle.size(), angle.type()); y.create(angle.size(), angle.type()); - if( magnitude.data ) + if ( magnitude.data ) { CV_Assert( magnitude.size() == angle.size() && magnitude.type() == angle.type() ); arithmetic_ptc_run(magnitude, angle, x, y, angleInDegrees, "arithm_polarToCart_mag"); @@ -1119,7 +1117,7 @@ static void arithmetic_minMaxLoc_mask_run(const oclMat &src, const oclMat &mask, vector > args; size_t gt[3] = {groupnum * 256, 1, 1}, lt[3] = {256, 1, 1}; char build_options[50]; - if(src.oclchannels() == 1) + if (src.oclchannels() == 1) { int cols = (src.cols - 1) / vlen + 1; int invalid_cols = src.step / (vlen * src.elemSize1()) - cols; @@ -1143,7 +1141,8 @@ static void arithmetic_minMaxLoc_mask_run(const oclMat &src, const oclMat &mask, openCLExecuteKernel(src.clCxt, &arithm_minMaxLoc_mask, "arithm_op_minMaxLoc_mask", gt, lt, args, -1, -1, build_options); } } -template + +template void arithmetic_minMaxLoc(const oclMat &src, double *minVal, double *maxVal, Point *minLoc, Point *maxLoc, const oclMat &mask) { @@ -1164,12 +1163,12 @@ void arithmetic_minMaxLoc(const oclMat &src, double *minVal, double *maxVal, T *p = new T[groupnum * vlen * 4]; memset(p, 0, dbsize); openCLReadBuffer(clCxt, dstBuffer, (void *)p, dbsize); - for(int i = 0; i < vlen * (int)groupnum; i++) + for (int i = 0; i < vlen * (int)groupnum; i++) { *minVal = (*minVal < p[i] || p[i + 2 * vlen * groupnum] == -1) ? *minVal : p[i]; minloc = (*minVal < p[i] || p[i + 2 * vlen * groupnum] == -1) ? minloc : cvRound(p[i + 2 * vlen * groupnum]); } - for(int i = vlen * (int)groupnum; i < 2 * vlen * (int)groupnum; i++) + for (int i = vlen * (int)groupnum; i < 2 * vlen * (int)groupnum; i++) { *maxVal = (*maxVal > p[i] || p[i + 2 * vlen * groupnum] == -1) ? *maxVal : p[i]; maxloc = (*maxVal > p[i] || p[i + 2 * vlen * groupnum] == -1) ? maxloc : cvRound(p[i + 2 * vlen * groupnum]); @@ -1178,9 +1177,9 @@ void arithmetic_minMaxLoc(const oclMat &src, double *minVal, double *maxVal, int pre_rows = src.offset / src.step; int pre_cols = (src.offset % src.step) / src.elemSize1(); int wholecols = src.step / src.elemSize1(); - if( minLoc ) + if ( minLoc ) { - if( minloc >= 0 ) + if ( minloc >= 0 ) { minLoc->y = minloc / wholecols - pre_rows; minLoc->x = minloc % wholecols - pre_cols; @@ -1188,9 +1187,9 @@ void arithmetic_minMaxLoc(const oclMat &src, double *minVal, double *maxVal, else minLoc->x = minLoc->y = -1; } - if( maxLoc ) + if ( maxLoc ) { - if( maxloc >= 0 ) + if ( maxloc >= 0 ) { maxLoc->y = maxloc / wholecols - pre_rows; maxLoc->x = maxloc % wholecols - pre_cols; @@ -1209,9 +1208,9 @@ typedef void (*minMaxLocFunc)(const oclMat &src, double *minVal, double *maxVal, void cv::ocl::minMaxLoc(const oclMat &src, double *minVal, double *maxVal, Point *minLoc, Point *maxLoc, const oclMat &mask) { - if(!src.clCxt->supportsFeature(Context::CL_DOUBLE) && src.depth() == CV_64F) + if (!src.clCxt->supportsFeature(Context::CL_DOUBLE) && src.depth() == CV_64F) { - CV_Error(CV_GpuNotSupported, "select device don't support double"); + CV_Error(CV_GpuNotSupported, "Selected device doesn't support double"); return; } @@ -1259,12 +1258,11 @@ static void arithmetic_countNonZero_run(const oclMat &src, cl_mem &dst, int vlen int cv::ocl::countNonZero(const oclMat &src) { size_t groupnum = src.clCxt->computeUnits(); - if(!src.clCxt->supportsFeature(Context::CL_DOUBLE) && src.depth() == CV_64F) + if (!src.clCxt->supportsFeature(Context::CL_DOUBLE) && src.depth() == CV_64F) { - CV_Error(CV_GpuNotSupported, "select device don't support double"); + CV_Error(CV_GpuNotSupported, "selected device doesn't support double"); } CV_Assert(groupnum != 0); -// groupnum = groupnum * 2; int vlen = 8 , dbsize = groupnum * vlen; Context *clCxt = src.clCxt; string kernelName = "arithm_op_nonzero"; @@ -1274,7 +1272,7 @@ int cv::ocl::countNonZero(const oclMat &src) memset(p, 0, dbsize * sizeof(int)); openCLReadBuffer(clCxt, dstBuffer, (void *)p, dbsize * sizeof(int)); - for(int i = 0; i < dbsize; i++) + for (int i = 0; i < dbsize; i++) nonzero += p[i]; delete[] p; @@ -1677,7 +1675,7 @@ static void arithmetic_pow_run(const oclMat &src1, double p, oclMat &dst, string args.push_back( make_pair( sizeof(cl_int), (void *)&dst_step1 )); float pf = static_cast(p); - if(!src1.clCxt->supportsFeature(Context::CL_DOUBLE)) + if (!src1.clCxt->supportsFeature(Context::CL_DOUBLE)) args.push_back( make_pair( sizeof(cl_float), (void *)&pf )); else args.push_back( make_pair( sizeof(cl_double), (void *)&p )); @@ -1687,7 +1685,7 @@ static void arithmetic_pow_run(const oclMat &src1, double p, oclMat &dst, string void cv::ocl::pow(const oclMat &x, double p, oclMat &y) { - if(!x.clCxt->supportsFeature(Context::CL_DOUBLE) && x.type() == CV_64F) + if (!x.clCxt->supportsFeature(Context::CL_DOUBLE) && x.type() == CV_64F) { cout << "Selected device do not support double" << endl; return; @@ -1714,14 +1712,14 @@ void cv::ocl::setIdentity(oclMat& src, double scalar) size_t global_threads[] = {src.cols, src.rows, 1}; string kernelName = "setIdentityKernel"; - if(src.type() == CV_32FC1) + if (src.type() == CV_32FC1) kernelName += "_F1"; - else if(src.type() == CV_32SC1) + else if (src.type() == CV_32SC1) kernelName += "_I1"; else { kernelName += "_D1"; - if(!(clCxt->supportsFeature(Context::CL_DOUBLE))) + if (!(clCxt->supportsFeature(Context::CL_DOUBLE))) { oclMat temp; src.convertTo(temp, CV_32FC1); @@ -1738,9 +1736,9 @@ void cv::ocl::setIdentity(oclMat& src, double scalar) int scalar_i = 0; float scalar_f = 0.0f; - if(clCxt->supportsFeature(Context::CL_DOUBLE)) + if (clCxt->supportsFeature(Context::CL_DOUBLE)) { - if(src.type() == CV_32SC1) + if (src.type() == CV_32SC1) { scalar_i = (int)scalar; args.push_back(make_pair(sizeof(cl_int), (void*)&scalar_i)); @@ -1750,7 +1748,7 @@ void cv::ocl::setIdentity(oclMat& src, double scalar) } else { - if(src.type() == CV_32SC1) + if (src.type() == CV_32SC1) { scalar_i = (int)scalar; args.push_back(make_pair(sizeof(cl_int), (void*)&scalar_i)); diff --git a/modules/ocl/src/opencl/arithm_minMaxLoc.cl b/modules/ocl/src/opencl/arithm_minMaxLoc.cl index 94cc14d258..848aac3197 100644 --- a/modules/ocl/src/opencl/arithm_minMaxLoc.cl +++ b/modules/ocl/src/opencl/arithm_minMaxLoc.cl @@ -142,29 +142,35 @@ #pragma OPENCL EXTENSION cl_khr_global_int32_extended_atomics:enable /**************************************Array minMax**************************************/ -__kernel void arithm_op_minMaxLoc (int cols,int invalid_cols,int offset,int elemnum,int groupnum, + +__kernel void arithm_op_minMaxLoc(int cols, int invalid_cols, int offset, int elemnum, int groupnum, __global VEC_TYPE *src, __global RES_TYPE *dst) { unsigned int lid = get_local_id(0); unsigned int gid = get_group_id(0); unsigned int id = get_global_id(0); unsigned int idx = offset + id + (id / cols) * invalid_cols; - __local VEC_TYPE localmem_max[128],localmem_min[128]; - VEC_TYPE minval,maxval,temp; - __local VEC_TYPE_LOC localmem_maxloc[128],localmem_minloc[128]; - VEC_TYPE_LOC minloc,maxloc,temploc,negative = -1; + + __local VEC_TYPE localmem_max[128], localmem_min[128]; + VEC_TYPE minval, maxval, temp; + + __local VEC_TYPE_LOC localmem_maxloc[128], localmem_minloc[128]; + VEC_TYPE_LOC minloc, maxloc, temploc, negative = -1; + int idx_c; - if(id < elemnum) + + if (id < elemnum) { temp = src[idx]; idx_c = idx << 2; - temploc = (VEC_TYPE_LOC)(idx_c,idx_c+1,idx_c+2,idx_c+3); - if(id % cols == 0 ) + temploc = (VEC_TYPE_LOC)(idx_c, idx_c + 1, idx_c + 2, idx_c + 3); + + if (id % cols == 0 ) { repeat_s(temp); repeat_s(temploc); } - if(id % cols == cols - 1) + if (id % cols == cols - 1) { repeat_e(temp); repeat_e(temploc); @@ -181,164 +187,33 @@ __kernel void arithm_op_minMaxLoc (int cols,int invalid_cols,int offset,int elem minloc = negative; maxloc = negative; } - float4 aaa; - for(id=id + (groupnum << 8); id < elemnum;id = id + (groupnum << 8)) + + int grainSize = (groupnum << 8); + for (id = id + grainSize; id < elemnum; id = id + grainSize) { idx = offset + id + (id / cols) * invalid_cols; temp = src[idx]; idx_c = idx << 2; - temploc = (VEC_TYPE_LOC)(idx_c,idx_c+1,idx_c+2,idx_c+3); - if(id % cols == 0 ) + temploc = (VEC_TYPE_LOC)(idx_c, idx_c+1, idx_c+2, idx_c+3); + + if (id % cols == 0 ) { repeat_s(temp); repeat_s(temploc); } - if(id % cols == cols - 1) + if (id % cols == cols - 1) { repeat_e(temp); repeat_e(temploc); } - minval = min(minval,temp); - maxval = max(maxval,temp); - minloc = CONDITION_FUNC(minval == temp, temploc , minloc); - maxloc = CONDITION_FUNC(maxval == temp, temploc , maxloc); - aaa= convert_float4(maxval == temp); - maxloc = convert_int4(aaa) ? temploc : maxloc; - } - if(lid > 127) - { - localmem_min[lid - 128] = minval; - localmem_max[lid - 128] = maxval; - localmem_minloc[lid - 128] = minloc; - localmem_maxloc[lid - 128] = maxloc; - } - barrier(CLK_LOCAL_MEM_FENCE); - if(lid < 128) - { - localmem_min[lid] = min(minval,localmem_min[lid]); - localmem_max[lid] = max(maxval,localmem_max[lid]); - localmem_minloc[lid] = CONDITION_FUNC(localmem_min[lid] == minval, minloc , localmem_minloc[lid]); - localmem_maxloc[lid] = CONDITION_FUNC(localmem_max[lid] == maxval, maxloc , localmem_maxloc[lid]); - } - barrier(CLK_LOCAL_MEM_FENCE); - for(int lsize = 64; lsize > 0; lsize >>= 1) - { - if(lid < lsize) - { - int lid2 = lsize + lid; - localmem_min[lid] = min(localmem_min[lid] , localmem_min[lid2]); - localmem_max[lid] = max(localmem_max[lid] , localmem_max[lid2]); - localmem_minloc[lid] = - CONDITION_FUNC(localmem_min[lid] == localmem_min[lid2], localmem_minloc[lid2] , localmem_minloc[lid]); - localmem_maxloc[lid] = - CONDITION_FUNC(localmem_max[lid] == localmem_max[lid2], localmem_maxloc[lid2] , localmem_maxloc[lid]); - } - barrier(CLK_LOCAL_MEM_FENCE); - } - if( lid == 0) - { - dst[gid] = CONVERT_RES_TYPE(localmem_min[0]); - dst[gid + groupnum] = CONVERT_RES_TYPE(localmem_max[0]); - dst[gid + 2 * groupnum] = CONVERT_RES_TYPE(localmem_minloc[0]); - dst[gid + 3 * groupnum] = CONVERT_RES_TYPE(localmem_maxloc[0]); - } -} -#if defined (REPEAT_S0) -#define repeat_ms(a) a = a; -#endif -#if defined (REPEAT_S1) -#define repeat_ms(a) a.s0 = 0; -#endif -#if defined (REPEAT_S2) -#define repeat_ms(a) a.s0 = 0;a.s1 = 0; -#endif -#if defined (REPEAT_S3) -#define repeat_ms(a) a.s0 = 0;a.s1 = 0;a.s2 = 0; -#endif - -#if defined (REPEAT_E0) -#define repeat_me(a) a = a; -#endif -#if defined (REPEAT_E1) -#define repeat_me(a) a.s3 = 0; -#endif -#if defined (REPEAT_E2) -#define repeat_me(a) a.s3 = 0;a.s2 = 0; -#endif -#if defined (REPEAT_E3) -#define repeat_me(a) a.s3 = 0;a.s2 = 0;a.s1 = 0; -#endif - - -/**************************************Array minMaxLoc mask**************************************/ -/* -__kernel void arithm_op_minMaxLoc_mask (int cols,int invalid_cols,int offset,int elemnum,int groupnum,__global VEC_TYPE *src, - int minvalid_cols,int moffset,__global uchar4 *mask,__global RES_TYPE *dst) -{ - unsigned int lid = get_local_id(0); - unsigned int gid = get_group_id(0); - unsigned int id = get_global_id(0); - unsigned int idx = offset + id + (id / cols) * invalid_cols; - unsigned int midx = moffset + id + (id / cols) * minvalid_cols; - __local VEC_TYPE localmem_max[128],localmem_min[128]; - VEC_TYPE minval,maxval,temp,max_val = MAX_VAL,min_val = MIN_VAL,zero = 0,m_temp; - __local VEC_TYPE_LOC localmem_maxloc[128],localmem_minloc[128]; - VEC_TYPE_LOC minloc,maxloc,temploc,negative = -1; - if(id < elemnum) - { - temp = src[idx]; - m_temp = CONVERT_TYPE(mask[midx]); - int idx_c = idx << 2; - temploc = (VEC_TYPE_LOC)(idx_c,idx_c+1,idx_c+2,idx_c+3); - if(id % cols == 0 ) - { - repeat_ms(m_temp); - repeat_s(temploc); - } - if(id % cols == cols - 1) - { - repeat_me(m_temp); - repeat_e(temploc); - } - minval = m_temp > zero ? temp : max_val; - maxval = m_temp > zero ? temp : min_val; - minloc = CONDITION_FUNC(m_temp > zero, temploc , negative); - maxloc = minloc; + minval = min(minval, temp); + maxval = max(maxval, temp); + minloc = CONDITION_FUNC(minval == temp, temploc, minloc); + maxloc = CONDITION_FUNC(maxval == temp, temploc, maxloc); } - else - { - minval = MAX_VAL; - maxval = MIN_VAL; - minloc = negative; - maxloc = negative; - } - for(id=id + (groupnum << 8); id < elemnum;id = id + (groupnum << 8)) - { - idx = offset + id + (id / cols) * invalid_cols; - midx = moffset + id + (id / cols) * minvalid_cols; - temp = src[idx]; - m_temp = CONVERT_TYPE(mask[midx]); - int idx_c = idx << 2; - temploc = (VEC_TYPE_LOC)(idx_c,idx_c+1,idx_c+2,idx_c+3); - if(id % cols == 0 ) - { - repeat_ms(m_temp); - repeat_s(temploc); - } - if(id % cols == cols - 1) - { - repeat_me(m_temp); - repeat_e(temploc); - } - minval = min(minval,m_temp > zero ? temp : max_val); - maxval = max(maxval,m_temp > zero ? temp : min_val); - temploc = CONDITION_FUNC(m_temp > zero, temploc , negative); - minloc = CONDITION_FUNC(minval == temp, temploc , minloc); - maxloc = CONDITION_FUNC(maxval == temp, temploc , maxloc); - } - if(lid > 127) + if (lid > 127) { localmem_min[lid - 128] = minval; localmem_max[lid - 128] = maxval; @@ -346,29 +221,30 @@ __kernel void arithm_op_minMaxLoc_mask (int cols,int invalid_cols,int offset,int localmem_maxloc[lid - 128] = maxloc; } barrier(CLK_LOCAL_MEM_FENCE); - if(lid < 128) + + if (lid < 128) { localmem_min[lid] = min(minval,localmem_min[lid]); localmem_max[lid] = max(maxval,localmem_max[lid]); - localmem_minloc[lid] = CONDITION_FUNC(localmem_min[lid] == minval, minloc , localmem_minloc[lid]); - localmem_maxloc[lid] = CONDITION_FUNC(localmem_max[lid] == maxval, maxloc , localmem_maxloc[lid]); + localmem_minloc[lid] = CONDITION_FUNC(localmem_min[lid] == minval, minloc, localmem_minloc[lid]); + localmem_maxloc[lid] = CONDITION_FUNC(localmem_max[lid] == maxval, maxloc, localmem_maxloc[lid]); } barrier(CLK_LOCAL_MEM_FENCE); - for(int lsize = 64; lsize > 0; lsize >>= 1) + + for (int lsize = 64; lsize > 0; lsize >>= 1) { - if(lid < lsize) + if (lid < lsize) { int lid2 = lsize + lid; - localmem_min[lid] = min(localmem_min[lid] , localmem_min[lid2]); - localmem_max[lid] = max(localmem_max[lid] , localmem_max[lid2]); - localmem_minloc[lid] = - CONDITION_FUNC(localmem_min[lid] == localmem_min[lid2], localmem_minloc[lid2] , localmem_minloc[lid]); - localmem_maxloc[lid] = - CONDITION_FUNC(localmem_max[lid] == localmem_max[lid2], localmem_maxloc[lid2] , localmem_maxloc[lid]); + localmem_min[lid] = min(localmem_min[lid], localmem_min[lid2]); + localmem_max[lid] = max(localmem_max[lid], localmem_max[lid2]); + localmem_minloc[lid] = CONDITION_FUNC(localmem_min[lid] == localmem_min[lid2], localmem_minloc[lid2], localmem_minloc[lid]); + localmem_maxloc[lid] = CONDITION_FUNC(localmem_max[lid] == localmem_max[lid2], localmem_maxloc[lid2], localmem_maxloc[lid]); } barrier(CLK_LOCAL_MEM_FENCE); } - if( lid == 0) + + if ( lid == 0) { dst[gid] = CONVERT_RES_TYPE(localmem_min[0]); dst[gid + groupnum] = CONVERT_RES_TYPE(localmem_max[0]); @@ -376,5 +252,3 @@ __kernel void arithm_op_minMaxLoc_mask (int cols,int invalid_cols,int offset,int dst[gid + 3 * groupnum] = CONVERT_RES_TYPE(localmem_maxloc[0]); } } - -*/ From c8821bd90914ab3682d47061fa5acada01b1ddb9 Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Wed, 25 Sep 2013 15:28:12 +0400 Subject: [PATCH 57/68] replaced manually new/delete by AutoBuffer --- modules/ocl/src/arithm.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/modules/ocl/src/arithm.cpp b/modules/ocl/src/arithm.cpp index 8d502ea56c..0dd695bfa6 100644 --- a/modules/ocl/src/arithm.cpp +++ b/modules/ocl/src/arithm.cpp @@ -354,13 +354,11 @@ Scalar arithmetic_sum(const oclMat &src, int type = 0) CV_Assert(groupnum != 0); int vlen = src.oclchannels() == 3 ? 12 : 8, dbsize = groupnum * vlen; Context *clCxt = src.clCxt; - T *p = new T[dbsize]; + + AutoBuffer _buf(dbsize); + T *p = (T*)_buf; cl_mem dstBuffer = openCLCreateBuffer(clCxt, CL_MEM_WRITE_ONLY, dbsize * sizeof(T)); - Scalar s; - s.val[0] = 0.0; - s.val[1] = 0.0; - s.val[2] = 0.0; - s.val[3] = 0.0; + Scalar s = Scalar::all(0.0); arithmetic_sum_buffer_run(src, dstBuffer, vlen, groupnum, type); memset(p, 0, dbsize * sizeof(T)); @@ -370,7 +368,7 @@ Scalar arithmetic_sum(const oclMat &src, int type = 0) for (int j = 0; j < src.oclchannels(); j++, i++) s.val[j] += p[i]; } - delete[] p; + openCLFree(dstBuffer); return s; } @@ -1160,8 +1158,10 @@ void arithmetic_minMaxLoc(const oclMat &src, double *minVal, double *maxVal, else arithmetic_minMaxLoc_mask_run(src, mask, dstBuffer, vlen, groupnum); - T *p = new T[groupnum * vlen * 4]; + AutoBuffer _buf(groupnum * vlen * 4); + T *p = (T*)_buf; memset(p, 0, dbsize); + openCLReadBuffer(clCxt, dstBuffer, (void *)p, dbsize); for (int i = 0; i < vlen * (int)groupnum; i++) { @@ -1197,7 +1197,6 @@ void arithmetic_minMaxLoc(const oclMat &src, double *minVal, double *maxVal, else maxLoc->x = maxLoc->y = -1; } - delete[] p; openCLSafeCall(clReleaseMemObject(dstBuffer)); } @@ -1266,7 +1265,9 @@ int cv::ocl::countNonZero(const oclMat &src) int vlen = 8 , dbsize = groupnum * vlen; Context *clCxt = src.clCxt; string kernelName = "arithm_op_nonzero"; - int *p = new int[dbsize], nonzero = 0; + + AutoBuffer _buf(dbsize); + int *p = (int*)_buf, nonzero = 0; cl_mem dstBuffer = openCLCreateBuffer(clCxt, CL_MEM_WRITE_ONLY, dbsize * sizeof(int)); arithmetic_countNonZero_run(src, dstBuffer, vlen, groupnum, kernelName); @@ -1275,7 +1276,6 @@ int cv::ocl::countNonZero(const oclMat &src) for (int i = 0; i < dbsize; i++) nonzero += p[i]; - delete[] p; openCLSafeCall(clReleaseMemObject(dstBuffer)); return nonzero; } From f5af3ab851195b337aef03451080d9182f867c7a Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Wed, 25 Sep 2013 16:18:04 +0400 Subject: [PATCH 58/68] changes in OpenCL matrix operations docs --- modules/ocl/doc/operations_on_matrices.rst | 333 ++++++++++----------- modules/ocl/include/opencv2/ocl/ocl.hpp | 121 ++++---- modules/ocl/src/matrix_operations.cpp | 13 +- 3 files changed, 225 insertions(+), 242 deletions(-) diff --git a/modules/ocl/doc/operations_on_matrices.rst b/modules/ocl/doc/operations_on_matrices.rst index e47e720922..39888e28cf 100644 --- a/modules/ocl/doc/operations_on_matrices.rst +++ b/modules/ocl/doc/operations_on_matrices.rst @@ -7,29 +7,29 @@ ocl::oclMat::convertTo ---------------------- Returns void -.. ocv:function:: void ocl::oclMat::convertTo( oclMat &m, int rtype, double alpha = 1, double beta = 0 ) const +.. ocv:function:: void ocl::oclMat::convertTo(oclMat &m, int rtype, double alpha = 1, double beta = 0) const - :param m: The destination matrix. If it does not have a proper size or type before the operation, it will be reallocated + :param m: the destination matrix. If it does not have a proper size or type before the operation, it will be reallocated. - :param rtype: The desired destination matrix type, or rather, the depth(since the number of channels will be the same with the source one). If rtype is negative, the destination matrix will have the same type as the source. + :param rtype: the desired destination matrix type, or rather, the depth (since the number of channels will be the same with the source one). If rtype is negative, the destination matrix will have the same type as the source. - :param alpha: must be default now + :param alpha: optional scale factor. - :param beta: must be default now + :param beta: optional delta added to the scaled values. -The method converts source pixel values to the target datatype. saturate cast is applied in the end to avoid possible overflows. Supports CV_8UC1, CV_8UC4, CV_32SC1, CV_32SC4, CV_32FC1, CV_32FC4. +The method converts source pixel values to the target datatype. Saturate cast is applied in the end to avoid possible overflows. Supports all data types. ocl::oclMat::copyTo ------------------- Returns void -.. ocv:function:: void ocl::oclMat::copyTo( oclMat &m, const oclMat &mask ) const +.. ocv:function:: void ocl::oclMat::copyTo(oclMat &m, const oclMat &mask = oclMat()) const - :param m: The destination matrix. If it does not have a proper size or type before the operation, it will be reallocated + :param m: The destination matrix. If it does not have a proper size or type before the operation, it will be reallocated. - :param mask(optional): The operation mask. Its non-zero elements indicate, which matrix elements need to be copied + :param mask: The operation mask. Its non-zero elements indicate, which matrix elements need to be copied. -Copies the matrix to another one. Supports CV_8UC1, CV_8UC4, CV_32SC1, CV_32SC4, CV_32FC1, CV_32FC4 +Copies the matrix to another one. Supports all data types. ocl::oclMat::setTo ------------------ @@ -37,171 +37,163 @@ Returns oclMat .. ocv:function:: oclMat& ocl::oclMat::setTo(const Scalar &s, const oclMat &mask = oclMat()) - :param s: Assigned scalar, which is converted to the actual array type + :param s: Assigned scalar, which is converted to the actual array type. - :param mask: The operation mask of the same size as ``*this`` + :param mask: The operation mask of the same size as ``*this`` and type ``CV_8UC1``. -Sets all or some of the array elements to the specified value. This is the advanced variant of Mat::operator=(const Scalar s) operator. Supports CV_8UC1, CV_8UC4, CV_32SC1, CV_32SC4, CV_32FC1, CV_32FC4. +Sets all or some of the array elements to the specified value. This is the advanced variant of Mat::operator=(const Scalar s) operator. Supports all data types. ocl::absdiff ------------------ Returns void -.. ocv:function:: void ocl::absdiff( const oclMat& a, const oclMat& b, oclMat& c ) +.. ocv:function:: void ocl::absdiff(const oclMat& src1, const oclMat& src2, oclMat& dst) -.. ocv:function:: void ocl::absdiff( const oclMat& a, const Scalar& s, oclMat& c ) +.. ocv:function:: void ocl::absdiff(const oclMat& src1, const Scalar& s, oclMat& dst) + :param src1: the first input array. - :param a: The first input array + :param src2: the second input array, must be the same size and same type as ``src1``. - :param b: The second input array, must be the same size and same type as a + :param s: scalar, the second input parameter. - :param s: Scalar, the second input parameter + :param dst: the destination array, it will have the same size and same type as ``src1``. - :param c: The destination array, it will have the same size and same type as a - -Computes per-element absolute difference between two arrays or between array and a scalar. Supports all data types except CV_8S. +Computes per-element absolute difference between two arrays or between array and a scalar. Supports all data types. ocl::add ------------------ Returns void -.. ocv:function:: void ocl::add( const oclMat & a, const oclMat & b, oclMat & c ) - -.. ocv:function:: void ocl::add( const oclMat & a, const oclMat & b, oclMat & c, const oclMat & mask ) +.. ocv:function:: void ocl::add(const oclMat & src1, const oclMat & src2, oclMat & dst, const oclMat & mask = oclMat()) -.. ocv:function:: void ocl::add( const oclMat & a, const Scalar & sc, oclMat & c, const oclMat & mask=oclMat() ) +.. ocv:function:: void ocl::add(const oclMat & src1, const Scalar & s, oclMat & dst, const oclMat & mask = oclMat()) - :param a: The first input array + :param src1: the first input array. - :param b: The second input array, must be the same size and same type as src1 + :param src2: the second input array, must be the same size and same type as ``src1``. - :param sc: Scalar, the second input parameter + :param s: scalar, the second input parameter - :param c: The destination array, it will have the same size and same type as src1 + :param dst: the destination array, it will have the same size and same type as ``src1``. - :param mask: he optional operation mask, 8-bit single channel array; specifies elements of the destination array to be changed + :param mask: the optional operation mask, 8-bit single channel array; specifies elements of the destination array to be changed. -Computes per-element additon between two arrays or between array and a scalar. Supports all data types except CV_8S. +Computes per-element additon between two arrays or between array and a scalar. Supports all data types. ocl::subtract ------------------ Returns void -.. ocv:function:: void ocl::subtract( const oclMat& a, const oclMat& b, oclMat& c ) - -.. ocv:function:: void ocl::subtract( const oclMat& a, const oclMat& b, oclMat& c, const oclMat& mask ) - -.. ocv:function:: void ocl::subtract( const oclMat& a, const Scalar& sc, oclMat& c, const oclMat& mask=oclMat() ) +.. ocv:function:: void ocl::subtract(const oclMat& src1, const oclMat& src2, oclMat& dst, const oclMat& mask = oclMat()) -.. ocv:function:: void ocl::subtract( const Scalar& sc, const oclMat& a, oclMat& c, const oclMat& mask=oclMat() ) +.. ocv:function:: void ocl::subtract(const oclMat& src1, const Scalar& s, oclMat& dst, const oclMat& mask = oclMat()) + :param src1: the first input array. - :param a: The first input array + :param src2: the second input array, must be the same size and same type as ``src1``. - :param b: The second input array, must be the same size and same type as src1 + :param s: scalar, the second input parameter. - :param sc: Scalar, the second input parameter + :param dst: the destination array, it will have the same size and same type as ``src1``. - :param c: The destination array, it will have the same size and same type as src1 + :param mask: the optional operation mask, 8-bit single channel array; specifies elements of the destination array to be changed. - :param mask: he optional operation mask, 8-bit single channel array; specifies elements of the destination array to be changed - -Computes per-element subtract between two arrays or between array and a scalar. Supports all data types except CV_8S. +Computes per-element subtract between two arrays or between array and a scalar. Supports all data types. ocl::multiply ------------------ Returns void -.. ocv:function:: void ocl::multiply( const oclMat& a, const oclMat& b, oclMat& c, double scale=1 ) +.. ocv:function:: void ocl::multiply(const oclMat& src1, const oclMat& src2, oclMat& dst, double scale = 1) - :param a: The first input array + :param src1: the first input array. - :param b: The second input array, must be the same size and same type as src1 + :param src2: the second input array, must be the same size and same type as ``src1``. - :param c: The destination array, it will have the same size and same type as src1 + :param dst: the destination array, it will have the same size and same type as ``src1``. - :param scale: must be 1 now + :param scale: optional scale factor. -Computes per-element multiply between two arrays or between array and a scalar. Supports all data types except CV_8S. +Computes per-element multiply between two arrays or between array and a scalar. Supports all data types. ocl::divide ------------------ Returns void -.. ocv:function:: void ocl::divide( const oclMat& a, const oclMat& b, oclMat& c, double scale=1 ) +.. ocv:function:: void ocl::divide(const oclMat& src1, const oclMat& src2, oclMat& dst, double scale = 1) -.. ocv:function:: void ocl::divide( double scale, const oclMat& b, oclMat& c ) +.. ocv:function:: void ocl::divide(double scale, const oclMat& src1, oclMat& dst) - :param a: The first input array + :param src1: the first input array. - :param b: The second input array, must be the same size and same type as src1 + :param src2: the second input array, must be the same size and same type as ``src1``. - :param c: The destination array, it will have the same size and same type as src1 + :param dst: the destination array, it will have the same size and same type as ``src1``. - :param scale: must be 1 now + :param scale: scalar factor. -Computes per-element divide between two arrays or between array and a scalar. Supports all data types except CV_8S. +Computes per-element divide between two arrays or between array and a scalar. Supports all data types. ocl::bitwise_and ------------------ Returns void -.. ocv:function:: void ocl::bitwise_and( const oclMat& src1, const oclMat& src2, oclMat& dst, const oclMat& mask=oclMat() ) +.. ocv:function:: void ocl::bitwise_and(const oclMat& src1, const oclMat& src2, oclMat& dst, const oclMat& mask = oclMat()) -.. ocv:function:: void ocl::bitwise_and( const oclMat& src1, const Scalar& s, oclMat& dst, const oclMat& mask=oclMat() ) +.. ocv:function:: void ocl::bitwise_and(const oclMat& src1, const Scalar& s, oclMat& dst, const oclMat& mask = oclMat()) - :param src1: The first input array + :param src1: the first input array. - :param src2: The second input array, must be the same size and same type as src1 + :param src2: the second input array, must be the same size and same type as ``src1``. - :param s: Scalar, the second input parameter + :param s: scalar, the second input parameter. - :param dst: The destination array, it will have the same size and same type as src1 + :param dst: the destination array, it will have the same size and same type as ``src1``. - :param mask: The optional operation mask, 8-bit single channel array; specifies elements of the destination array to be changed + :param mask: the optional operation mask, 8-bit single channel array; specifies elements of the destination array to be changed. -Computes per-element bitwise_and between two arrays or between array and a scalar. Supports all data types except CV_8S. +Computes per-element bitwise_and between two arrays or between array and a scalar. Supports all data types. ocl::bitwise_or ------------------ Returns void -.. ocv:function:: void ocl::bitwise_or( const oclMat& src1, const oclMat& src2, oclMat& dst, const oclMat& mask=oclMat() ) +.. ocv:function:: void ocl::bitwise_or(const oclMat& src1, const oclMat& src2, oclMat& dst, const oclMat& mask = oclMat()) -.. ocv:function:: void ocl::bitwise_or( const oclMat& src1, const Scalar& s, oclMat& dst, const oclMat& mask=oclMat() ) +.. ocv:function:: void ocl::bitwise_or(const oclMat& src1, const Scalar& s, oclMat& dst, const oclMat& mask = oclMat()) - :param src1: The first input array + :param src1: the first input array. - :param src2: The second input array, must be the same size and same type as src1 + :param src2: the second input array, must be the same size and same type as ``src1``. - :param s: Scalar, the second input parameter + :param s: scalar, the second input parameter. - :param dst: The destination array, it will have the same size and same type as src1 + :param dst: the destination array, it will have the same size and same type as ``src1``. - :param mask: The optional operation mask, 8-bit single channel array; specifies elements of the destination array to be changed + :param mask: the optional operation mask, 8-bit single channel array; specifies elements of the destination array to be changed. -Computes per-element bitwise_or between two arrays or between array and a scalar. Supports all data types except CV_8S. +Computes per-element bitwise_or between two arrays or between array and a scalar. Supports all data types. ocl::bitwise_xor ------------------ Returns void -.. ocv:function:: void ocl::bitwise_xor( const oclMat& src1, const oclMat& src2, oclMat& dst, const oclMat& mask=oclMat() ) +.. ocv:function:: void ocl::bitwise_xor(const oclMat& src1, const oclMat& src2, oclMat& dst, const oclMat& mask = oclMat()) -.. ocv:function:: void ocl::bitwise_xor( const oclMat& src1, const Scalar& s, oclMat& dst, const oclMat& mask=oclMat() ) +.. ocv:function:: void ocl::bitwise_xor(const oclMat& src1, const Scalar& s, oclMat& dst, const oclMat& mask = oclMat()) - :param src1: The first input array + :param src1: the first input array. - :param src2: The second input array, must be the same size and same type as src1 + :param src2: the second input array, must be the same size and same type as ``src1``. - :param sc: Scalar, the second input parameter + :param sc: scalar, the second input parameter. - :param dst: The destination array, it will have the same size and same type as src1 + :param dst: the destination array, it will have the same size and same type as ``src1``. - :param mask: The optional operation mask, 8-bit single channel array; specifies elements of the destination array to be changed + :param mask: the optional operation mask, 8-bit single channel array; specifies elements of the destination array to be changed. -Computes per-element bitwise_xor between two arrays or between array and a scalar. Supports all data types except CV_8S. +Computes per-element bitwise_xor between two arrays or between array and a scalar. Supports all data types. ocl::bitwise_not ------------------ @@ -209,11 +201,11 @@ Returns void .. ocv:function:: void ocl::bitwise_not(const oclMat &src, oclMat &dst) - :param src: The input array + :param src: the input array. - :param dst: The destination array, it will have the same size and same type as src1 + :param dst: the destination array, it will have the same size and same type as ``src``. -The functions bitwise not compute per-element bit-wise inversion of the source array:. Supports all data types except CV_8S. +The functions bitwise not compute per-element bit-wise inversion of the source array. Supports all data types. ocl::cartToPolar ------------------ @@ -221,17 +213,17 @@ Returns void .. ocv:function:: void ocl::cartToPolar(const oclMat &x, const oclMat &y, oclMat &magnitude, oclMat &angle, bool angleInDegrees = false) - :param x: The array of x-coordinates; must be single-precision or double-precision floating-point array + :param x: the array of x-coordinates; must be single-precision or double-precision floating-point array. - :param y: The array of y-coordinates; it must have the same size and same type as x + :param y: the array of y-coordinates; it must have the same size and same type as ``x``. - :param magnitude: The destination array of magnitudes of the same size and same type as x + :param magnitude: the destination array of magnitudes of the same size and same type as ``x``. - :param angle: The destination array of angles of the same size and same type as x. The angles are measured in radians (0 to 2pi ) or in degrees (0 to 360 degrees). + :param angle: the destination array of angles of the same size and same type as ``x``. The angles are measured in radians (0 to 2pi) or in degrees (0 to 360 degrees). - :param angleInDegrees: The flag indicating whether the angles are measured in radians, which is default mode, or in degrees + :param angleInDegrees: the flag indicating whether the angles are measured in radians, which is default mode, or in degrees. -Calculates the magnitude and angle of 2d vectors. Supports only CV_32F and CV_64F data types. +Calculates the magnitude and angle of 2D vectors. Supports only ``CV_32F`` and ``CV_64F`` data types. ocl::polarToCart ------------------ @@ -239,57 +231,57 @@ Returns void .. ocv:function:: void ocl::polarToCart(const oclMat &magnitude, const oclMat &angle, oclMat &x, oclMat &y, bool angleInDegrees = false) - :param magnitude: The source floating-point array of magnitudes of 2D vectors. It can be an empty matrix (=Mat()) - in this case the function assumes that all the magnitudes are =1. If it's not empty, it must have the same size and same type as angle + :param magnitude: the source floating-point array of magnitudes of 2D vectors. It can be an empty matrix (=Mat()) - in this case the function assumes that all the magnitudes are = 1. If it's not empty, it must have the same size and same type as ``angle``. - :param angle: The source floating-point array of angles of the 2D vectors + :param angle: the source floating-point array of angles of the 2D vectors. - :param x: The destination array of x-coordinates of 2D vectors; will have the same size and the same type as angle + :param x: the destination array of x-coordinates of 2D vectors; will have the same size and the same type as ``angle``. - :param y: The destination array of y-coordinates of 2D vectors; will have the same size and the same type as angle + :param y: the destination array of y-coordinates of 2D vectors; will have the same size and the same type as ``angle``. - :param angleInDegrees: The flag indicating whether the angles are measured in radians, which is default mode, or in degrees + :param angleInDegrees: the flag indicating whether the angles are measured in radians, which is default mode, or in degrees. -The function polarToCart computes the cartesian coordinates of each 2D vector represented by the corresponding elements of magnitude and angle. Supports only CV_32F and CV_64F data types. +The function polarToCart computes the cartesian coordinates of each 2D vector represented by the corresponding elements of magnitude and angle. Supports only ``CV_32F`` and ``CV_64F`` data types. ocl::compare ------------------ Returns void -.. ocv:function:: void ocl::compare(const oclMat &a, const oclMat &b, oclMat &c, int cmpop) +.. ocv:function:: void ocl::compare(const oclMat &src1, const oclMat &src2, oclMat &dst, int cmpop) - :param a: The first source array + :param src1: the first source array. - :param b: The second source array; must have the same size and same type as a + :param src2: the second source array; must have the same size and same type as ``src1``. - :param c: The destination array; will have the same size as a + :param dst: the destination array; will have the same size as ``src1`` and type ``CV_8UC1``. - :param cmpop: The flag specifying the relation between the elements to be checked + :param cmpop: the flag specifying the relation between the elements to be checked. -Performs per-element comparison of two arrays or an array and scalar value. Supports all the 1 channel data types except CV_8S. +Performs per-element comparison of two arrays or an array and scalar value. Supports all data types. ocl::exp ------------------ Returns void -.. ocv:function:: void ocl::exp(const oclMat &a, oclMat &b) +.. ocv:function:: void ocl::exp(const oclMat &src, oclMat &dst) - :param a: The first source array + :param src: the first source array. - :param b: The dst array; must have the same size and same type as a + :param dst: the dst array; must have the same size and same type as ``src``. -The function exp calculates the exponent of every element of the input array. Supports only CV_32FC1 data type. +The function exp calculates the exponent of every element of the input array. Supports only ``CV_32FC1`` and ``CV_64F`` data types. ocl::log ------------------ Returns void -.. ocv:function:: void ocl::log(const oclMat &a, oclMat &b) +.. ocv:function:: void ocl::log(const oclMat &src, oclMat &dst) - :param a: The first source array + :param src: the first source array. - :param b: The dst array; must have the same size and same type as a + :param dst: the dst array; must have the same size and same type as ``src``. -The function log calculates the log of every element of the input array. Supports only CV_32FC1 data type. +The function log calculates the log of every element of the input array. Supports only ``CV_32FC1`` and ``CV_64F`` data types. ocl::LUT ------------------ @@ -297,13 +289,13 @@ Returns void .. ocv:function:: void ocl::LUT(const oclMat &src, const oclMat &lut, oclMat &dst) - :param src: Source array of 8-bit elements + :param src: source array of 8-bit elements. - :param lut: Look-up table of 256 elements. In the case of multi-channel source array, the table should either have a single channel (in this case the same table is used for all channels) or the same number of channels as in the source array + :param lut: look-up table of 256 elements. In the case of multi-channel source array, the table should either have a single channel (in this case the same table is used for all channels) or the same number of channels as in the source array. - :param dst: Destination array; will have the same size and the same number of channels as src, and the same depth as lut + :param dst: destination array; will have the same size and the same number of channels as ``src``, and the same depth as ``lut``. -Performs a look-up table transform of an array. Supports only CV_8UC1 and CV_8UC4 data type. +Performs a look-up table transform of an array. ocl::magnitude ------------------ @@ -311,25 +303,25 @@ Returns void .. ocv:function:: void ocl::magnitude(const oclMat &x, const oclMat &y, oclMat &magnitude) - :param x: The floating-point array of x-coordinates of the vectors + :param x: the floating-point array of x-coordinates of the vectors. - :param y: he floating-point array of y-coordinates of the vectors; must have the same size as x + :param y: the floating-point array of y-coordinates of the vectors; must have the same size as ``x``. - :param magnitude: The destination array; will have the same size and same type as x + :param magnitude: the destination array; will have the same size and same type as ``x``. -The function magnitude calculates magnitude of 2D vectors formed from the corresponding elements of x and y arrays. Supports only CV_32F and CV_64F data type. +The function magnitude calculates magnitude of 2D vectors formed from the corresponding elements of ``x`` and ``y`` arrays. Supports only ``CV_32F`` and ``CV_64F`` data types. ocl::flip ------------------ Returns void -.. ocv:function:: void ocl::flip( const oclMat& a, oclMat& b, int flipCode ) +.. ocv:function:: void ocl::flip(const oclMat& src, oclMat& dst, int flipCode) - :param a: Source image. + :param src: source image. - :param b: Destination image + :param dst: destination image. - :param flipCode: Specifies how to flip the array: 0 means flipping around the x-axis, positive (e.g., 1) means flipping around y-axis, and negative (e.g., -1) means flipping around both axes. + :param flipCode: specifies how to flip the array: 0 means flipping around the x-axis, positive (e.g., 1) means flipping around y-axis, and negative (e.g., -1) means flipping around both axes. The function flip flips the array in one of three different ways (row and column indices are 0-based). Supports all data types. @@ -339,13 +331,13 @@ Returns void .. ocv:function:: void ocl::meanStdDev(const oclMat &mtx, Scalar &mean, Scalar &stddev) - :param mtx: Source image. + :param mtx: source image. - :param mean: The output parameter: computed mean value + :param mean: the output parameter: computed mean value. - :param stddev: The output parameter: computed standard deviation + :param stddev: the output parameter: computed standard deviation. -The functions meanStdDev compute the mean and the standard deviation M of array elements, independently for each channel, and return it via the output parameters. Supports all data types except CV_32F,CV_64F +The functions meanStdDev compute the mean and the standard deviation M of array elements, independently for each channel, and return it via the output parameters. Supports all data types except ``CV_32F``, ``CV_64F``. ocl::merge ------------------ @@ -353,9 +345,9 @@ Returns void .. ocv:function:: void ocl::merge(const vector &src, oclMat &dst) - :param src: The source array or vector of the single-channel matrices to be merged. All the matrices in src must have the same size and the same type + :param src: The source array or vector of the single-channel matrices to be merged. All the matrices in src must have the same size and the same type. - :param dst: The destination array; will have the same size and the same depth as src, the number of channels will match the number of source matrices + :param dst: The destination array; will have the same size and the same depth as src, the number of channels will match the number of source matrices. Composes a multi-channel array from several single-channel arrays. Supports all data types. @@ -379,13 +371,13 @@ Returns the calculated norm .. ocv:function:: double ocl::norm(const oclMat &src1, const oclMat &src2, int normType = NORM_L2) - :param src1: The first source array + :param src1: the first source array. - :param src2: The second source array of the same size and the same type as src1 + :param src2: the second source array of the same size and the same type as ``src1``. - :param normType: Type of the norm + :param normType: type of the norm. -Calculates absolute array norm, absolute difference norm, or relative difference norm. Supports only CV_8UC1 data type. +Calculates absolute array norm, absolute difference norm, or relative difference norm. Supports only ``CV_8UC1`` data type. ocl::phase ------------------ @@ -393,15 +385,15 @@ Returns void .. ocv:function:: void ocl::phase(const oclMat &x, const oclMat &y, oclMat &angle, bool angleInDegrees = false) - :param x: The source floating-point array of x-coordinates of 2D vectors + :param x: the source floating-point array of x-coordinates of 2D vectors - :param y: The source array of y-coordinates of 2D vectors; must have the same size and the same type as x + :param y: the source array of y-coordinates of 2D vectors; must have the same size and the same type as ``x``. - :param angle: The destination array of vector angles; it will have the same size and same type as x + :param angle: the destination array of vector angles; it will have the same size and same type as ``x``. - :param angleInDegrees: When it is true, the function will compute angle in degrees, otherwise they will be measured in radians + :param angleInDegrees: when it is true, the function will compute angle in degrees, otherwise they will be measured in radians. -The function phase computes the rotation angle of each 2D vector that is formed from the corresponding elements of x and y. Supports only CV_32FC1 and CV_64FC1 data type. +The function phase computes the rotation angle of each 2D vector that is formed from the corresponding elements of ``x`` and ``y``. Supports only ``CV_32FC1`` and ``CV_64FC1`` data type. ocl::pow ------------------ @@ -409,13 +401,13 @@ Returns void .. ocv:function:: void ocl::pow(const oclMat &x, double p, oclMat &y) - :param x: The source array + :param x: the source array. - :param power: The exponent of power;The source floating-point array of angles of the 2D vectors + :param p: the exponent of power; the source floating-point array of angles of the 2D vectors. - :param y: The destination array, should be the same type as the source + :param y: the destination array, should be the same type as the source. -The function pow raises every element of the input array to p. Supports only CV_32FC1 and CV_64FC1 data type. +The function pow raises every element of the input array to ``p``. Supports only ``CV_32FC1`` and ``CV_64FC1`` data types. ocl::transpose ------------------ @@ -423,26 +415,26 @@ Returns void .. ocv:function:: void ocl::transpose(const oclMat &src, oclMat &dst) - :param src: The source array + :param src: the source array. - :param dst: The destination array of the same type as src + :param dst: the destination array of the same type as ``src``. -Transposes a matrix. Supports 8UC1, 8UC4, 8SC4, 16UC2, 16SC2, 32SC1 and 32FC1 data types. +Transposes a matrix (in case when ``src`` == ``dst`` and matrix is square the operation are performed inplace) ocl::dft ------------ Performs a forward or inverse discrete Fourier transform (1D or 2D) of the floating point matrix. -.. ocv:function:: void ocl::dft( const oclMat& src, oclMat& dst, Size dft_size=Size(0, 0), int flags=0 ) +.. ocv:function:: void ocl::dft(const oclMat& src, oclMat& dst, Size dft_size = Size(), int flags = 0) - :param src: Source matrix (real or complex). + :param src: source matrix (real or complex). - :param dst: Destination matrix (real or complex). + :param dst: destination matrix (real or complex). - :param dft_size: Size of original input, which is used for transformation from complex to real. + :param dft_size: size of original input, which is used for transformation from complex to real. - :param flags: Optional flags: + :param flags: optional flags: * **DFT_ROWS** transforms each individual row of the source matrix. @@ -452,9 +444,9 @@ Performs a forward or inverse discrete Fourier transform (1D or 2D) of the float * **DFT_REAL_OUTPUT** specifies the output as real. The source matrix is the result of real-complex transform, so the destination matrix must be real. -Use to handle real matrices ( ``CV32FC1`` ) and complex matrices in the interleaved format ( ``CV32FC2`` ). +Use to handle real matrices (``CV_32FC1``) and complex matrices in the interleaved format (``CV_32FC2``). -The dft_size must be powers of 2, 3 and 5. Real to complex dft output is not the same with cpu version. real to complex and complex to real does not support DFT_ROWS +The ``dft_size`` must be powers of ``2``, ``3`` and ``5``. Real to complex dft output is not the same with cpu version. Real to complex and complex to real does not support ``DFT_ROWS``. .. seealso:: :ocv:func:`dft` @@ -464,22 +456,22 @@ Performs generalized matrix multiplication. .. ocv:function:: void ocl::gemm(const oclMat& src1, const oclMat& src2, double alpha, const oclMat& src3, double beta, oclMat& dst, int flags = 0) - :param src1: First multiplied input matrix that should be ``CV_32FC1`` type. + :param src1: first multiplied input matrix that should be ``CV_32FC1`` type. - :param src2: Second multiplied input matrix of the same type as ``src1`` . + :param src2: second multiplied input matrix of the same type as ``src1``. - :param alpha: Weight of the matrix product. + :param alpha: weight of the matrix product. - :param src3: Third optional delta matrix added to the matrix product. It should have the same type as ``src1`` and ``src2`` . + :param src3: third optional delta matrix added to the matrix product. It should have the same type as ``src1`` and ``src2``. - :param beta: Weight of ``src3`` . + :param beta: weight of ``src3``. - :param dst: Destination matrix. It has the proper size and the same type as input matrices. + :param dst: destination matrix. It has the proper size and the same type as input matrices. - :param flags: Operation flags: + :param flags: operation flags: - * **GEMM_1_T** transpose ``src1`` - * **GEMM_2_T** transpose ``src2`` + * **GEMM_1_T** transpose ``src1``. + * **GEMM_2_T** transpose ``src2``. .. seealso:: :ocv:func:`gemm` @@ -489,28 +481,29 @@ Returns void .. ocv:function:: void ocl::sortByKey(oclMat& keys, oclMat& values, int method, bool isGreaterThan = false) - :param keys: The keys to be used as sorting indices. + :param keys: the keys to be used as sorting indices. - :param values: The array of values. + :param values: the array of values. - :param isGreaterThan: Determine sorting order. + :param isGreaterThan: determine sorting order. :param method: supported sorting methods: - * **SORT_BITONIC** bitonic sort, only support power-of-2 buffer size - * **SORT_SELECTION** selection sort, currently cannot sort duplicate keys - * **SORT_MERGE** merge sort - * **SORT_RADIX** radix sort, only support signed int/float keys(``CV_32S``/``CV_32F``) + + * **SORT_BITONIC** bitonic sort, only support power-of-2 buffer size. + * **SORT_SELECTION** selection sort, currently cannot sort duplicate keys. + * **SORT_MERGE** merge sort. + * **SORT_RADIX** radix sort, only support signed int/float keys(``CV_32S``/``CV_32F``). Returns the sorted result of all the elements in values based on equivalent keys. -The element unit in the values to be sorted is determined from the data type, -i.e., a ``CV_32FC2`` input ``{a1a2, b1b2}`` will be considered as two elements, regardless its matrix dimension. +The element unit in the values to be sorted is determined from the data type, i.e., a ``CV_32FC2`` input ``{a1a2, b1b2}`` will be considered as two elements, regardless its matrix dimension. Both keys and values will be sorted inplace. -Keys needs to be a **single** channel `oclMat`. +Keys needs to be a **single** channel ``oclMat``. Example:: + input - keys = {2, 3, 1} (CV_8UC1) values = {10,5, 4,3, 6,2} (CV_8UC2) diff --git a/modules/ocl/include/opencv2/ocl/ocl.hpp b/modules/ocl/include/opencv2/ocl/ocl.hpp index d46ad503e8..d3dbded34d 100644 --- a/modules/ocl/include/opencv2/ocl/ocl.hpp +++ b/modules/ocl/include/opencv2/ocl/ocl.hpp @@ -268,13 +268,12 @@ namespace cv //! returns deep copy of the oclMatrix, i.e. the data is copied oclMat clone() const; - //! copies the oclMatrix content to "m". + + //! copies those oclMatrix elements to "m" that are marked with non-zero mask elements. // It calls m.create(this->size(), this->type()). // It supports any data type - void copyTo( oclMat &m ) const; - //! copies those oclMatrix elements to "m" that are marked with non-zero mask elements. - //It supports 8UC1 8UC4 32SC1 32SC4 32FC1 32FC4 - void copyTo( oclMat &m, const oclMat &mask ) const; + void copyTo( oclMat &m, const oclMat &mask = oclMat()) const; + //! converts oclMatrix to another datatype with optional scalng. See cvConvertScale. //It supports 8UC1 8UC4 32SC1 32SC4 32FC1 32FC4 void convertTo( oclMat &m, int rtype, double alpha = 1, double beta = 0 ) const; @@ -410,57 +409,51 @@ namespace cv ////////////////////////////// Arithmetics /////////////////////////////////// - //#if defined DOUBLE_SUPPORT - //typedef double F; - //#else - //typedef float F; - //#endif - - // CV_EXPORTS void addWeighted(const oclMat& a,F alpha, const oclMat& b,F beta,F gama, oclMat& c); - CV_EXPORTS void addWeighted(const oclMat &a, double alpha, const oclMat &b, double beta, double gama, oclMat &c); - - //! adds one matrix to another (c = a + b) - // supports all types except CV_8SC1,CV_8SC2,CV8SC3 and CV_8SC4 - CV_EXPORTS void add(const oclMat &a, const oclMat &b, oclMat &c, const oclMat &mask = oclMat()); - //! adds scalar to a matrix (c = a + s) - // supports all types except CV_8SC1,CV_8SC2,CV8SC3 and CV_8SC4 - CV_EXPORTS void add(const oclMat &a, const Scalar &sc, oclMat &c, const oclMat &mask = oclMat()); - - //! subtracts one matrix from another (c = a - b) - // supports all types except CV_8SC1,CV_8SC2,CV8SC3 and CV_8SC4 - CV_EXPORTS void subtract(const oclMat &a, const oclMat &b, oclMat &c, const oclMat &mask = oclMat()); - //! subtracts scalar from a matrix (c = a - s) - // supports all types except CV_8SC1,CV_8SC2,CV8SC3 and CV_8SC4 - CV_EXPORTS void subtract(const oclMat &a, const Scalar &sc, oclMat &c, const oclMat &mask = oclMat()); - - //! computes element-wise product of the two arrays (c = a * b) - // supports all types except CV_8SC1,CV_8SC2,CV8SC3 and CV_8SC4 - CV_EXPORTS void multiply(const oclMat &a, const oclMat &b, oclMat &c, double scale = 1); + //! adds one matrix to another with scale (dst = src1 * alpha + src2 * beta + gama) + CV_EXPORTS void addWeighted(const oclMat &src1, double alpha, const oclMat &src2, double beta, double gama, oclMat &dst); + + //! adds one matrix to another (dst = src1 + src2) + // supports all data types + CV_EXPORTS void add(const oclMat &src1, const oclMat &src2, oclMat &dst, const oclMat &mask = oclMat()); + //! adds scalar to a matrix (dst = src1 + s) + // supports all data types + CV_EXPORTS void add(const oclMat &src1, const Scalar &s, oclMat &dst, const oclMat &mask = oclMat()); + + //! subtracts one matrix from another (dst = src1 - src2) + // supports all data types + CV_EXPORTS void subtract(const oclMat &src1, const oclMat &src2, oclMat &dst, const oclMat &mask = oclMat()); + //! subtracts scalar from a matrix (dst = src1 - s) + // supports all data types + CV_EXPORTS void subtract(const oclMat &src1, const Scalar &s, oclMat &dst, const oclMat &mask = oclMat()); + + //! computes element-wise product of the two arrays (dst = src1 * scale * src2) + // supports all data types + CV_EXPORTS void multiply(const oclMat &src1, const oclMat &src2, oclMat &dst, double scale = 1); //! multiplies matrix to a number (dst = scalar * src) - // supports CV_32FC1 only + // supports all data types CV_EXPORTS void multiply(double scalar, const oclMat &src, oclMat &dst); - //! computes element-wise quotient of the two arrays (c = a / b) - // supports all types except CV_8SC1,CV_8SC2,CV8SC3 and CV_8SC4 - CV_EXPORTS void divide(const oclMat &a, const oclMat &b, oclMat &c, double scale = 1); - //! computes element-wise quotient of the two arrays (c = a / b) - // supports all types except CV_8SC1,CV_8SC2,CV8SC3 and CV_8SC4 - CV_EXPORTS void divide(double scale, const oclMat &b, oclMat &c); + //! computes element-wise quotient of the two arrays (dst = src1 * scale / src2) + // supports all data types + CV_EXPORTS void divide(const oclMat &src1, const oclMat &src2, oclMat &dst, double scale = 1); + //! computes element-wise quotient of the two arrays (dst = scale / src) + // supports all data types + CV_EXPORTS void divide(double scale, const oclMat &src1, oclMat &dst); - //! compares elements of two arrays (c = a b) - // supports except CV_8SC1,CV_8SC2,CV8SC3,CV_8SC4 types - CV_EXPORTS void compare(const oclMat &a, const oclMat &b, oclMat &c, int cmpop); + //! compares elements of two arrays (dst = src1 src2) + // supports all data types + CV_EXPORTS void compare(const oclMat &src1, const oclMat &src2, oclMat &dst, int cmpop); //! transposes the matrix - // supports CV_8UC1, 8UC4, 8SC4, 16UC2, 16SC2, 32SC1 and 32FC1.(the same as cuda) + // supports all data types CV_EXPORTS void transpose(const oclMat &src, oclMat &dst); - //! computes element-wise absolute difference of two arrays (c = abs(a - b)) - // supports all types except CV_8SC1,CV_8SC2,CV8SC3 and CV_8SC4 - CV_EXPORTS void absdiff(const oclMat &a, const oclMat &b, oclMat &c); - //! computes element-wise absolute difference of array and scalar (c = abs(a - s)) - // supports all types except CV_8SC1,CV_8SC2,CV8SC3 and CV_8SC4 - CV_EXPORTS void absdiff(const oclMat &a, const Scalar &s, oclMat &c); + //! computes element-wise absolute difference of two arrays (dst = abs(src1 - src2)) + // supports all data types + CV_EXPORTS void absdiff(const oclMat &src1, const oclMat &src2, oclMat &dst); + //! computes element-wise absolute difference of array and scalar (dst = abs(src1 - s)) + // supports all data types + CV_EXPORTS void absdiff(const oclMat &src1, const Scalar &s, oclMat &dst); //! computes mean value and standard deviation of all or selected array elements // supports except CV_32F,CV_64F @@ -478,7 +471,7 @@ namespace cv //! reverses the order of the rows, columns or both in a matrix // supports all types - CV_EXPORTS void flip(const oclMat &a, oclMat &b, int flipCode); + CV_EXPORTS void flip(const oclMat &src, oclMat &dst, int flipCode); //! computes sum of array elements // disabled until fix crash @@ -489,13 +482,11 @@ namespace cv //! finds global minimum and maximum array elements and returns their values // support all C1 types - CV_EXPORTS void minMax(const oclMat &src, double *minVal, double *maxVal = 0, const oclMat &mask = oclMat()); CV_EXPORTS void minMax_buf(const oclMat &src, double *minVal, double *maxVal, const oclMat &mask, oclMat& buf); //! finds global minimum and maximum array elements and returns their values with locations // support all C1 types - CV_EXPORTS void minMaxLoc(const oclMat &src, double *minVal, double *maxVal = 0, Point *minLoc = 0, Point *maxLoc = 0, const oclMat &mask = oclMat()); @@ -524,27 +515,27 @@ namespace cv // This is not truly a bilateral filter. Instead of using user provided fixed parameters, // the function calculates a constant at each window based on local standard deviation, // and use this constant to do filtering. - // supports 8UC1 8UC3 + // supports 8UC1, 8UC3 CV_EXPORTS void adaptiveBilateralFilter(const oclMat& src, oclMat& dst, Size ksize, double sigmaSpace, Point anchor = Point(-1, -1), int borderType=BORDER_DEFAULT); - //! computes exponent of each matrix element (b = e**a) - // supports only CV_32FC1 type - CV_EXPORTS void exp(const oclMat &a, oclMat &b); + //! computes exponent of each matrix element (dst = e**src) + // supports only CV_32FC1, CV_64FC1 type + CV_EXPORTS void exp(const oclMat &src, oclMat &dst); - //! computes natural logarithm of absolute value of each matrix element: b = log(abs(a)) - // supports only CV_32FC1 type - CV_EXPORTS void log(const oclMat &a, oclMat &b); + //! computes natural logarithm of absolute value of each matrix element: dst = log(abs(src)) + // supports only CV_32FC1, CV_64FC1 type + CV_EXPORTS void log(const oclMat &src, oclMat &dst); //! computes magnitude of each (x(i), y(i)) vector - // supports only CV_32F CV_64F type + // supports only CV_32F, CV_64F type CV_EXPORTS void magnitude(const oclMat &x, const oclMat &y, oclMat &magnitude); //! computes angle (angle(i)) of each (x(i), y(i)) vector - // supports only CV_32F CV_64F type + // supports only CV_32F, CV_64F type CV_EXPORTS void phase(const oclMat &x, const oclMat &y, oclMat &angle, bool angleInDegrees = false); //! the function raises every element of tne input array to p - //! support only CV_32F CV_64F type + // support only CV_32F, CV_64F type CV_EXPORTS void pow(const oclMat &x, double p, oclMat &y); //! converts Cartesian coordinates to polar @@ -558,14 +549,17 @@ namespace cv //! perfroms per-elements bit-wise inversion // supports all types CV_EXPORTS void bitwise_not(const oclMat &src, oclMat &dst); + //! calculates per-element bit-wise disjunction of two arrays // supports all types CV_EXPORTS void bitwise_or(const oclMat &src1, const oclMat &src2, oclMat &dst, const oclMat &mask = oclMat()); CV_EXPORTS void bitwise_or(const oclMat &src1, const Scalar &s, oclMat &dst, const oclMat &mask = oclMat()); + //! calculates per-element bit-wise conjunction of two arrays // supports all types CV_EXPORTS void bitwise_and(const oclMat &src1, const oclMat &src2, oclMat &dst, const oclMat &mask = oclMat()); CV_EXPORTS void bitwise_and(const oclMat &src1, const Scalar &s, oclMat &dst, const oclMat &mask = oclMat()); + //! calculates per-element bit-wise "exclusive or" operation // supports all types CV_EXPORTS void bitwise_xor(const oclMat &src1, const oclMat &src2, oclMat &dst, const oclMat &mask = oclMat()); @@ -585,12 +579,13 @@ namespace cv CV_EXPORTS oclMatExpr operator / (const oclMat &src1, const oclMat &src2); //! computes convolution of two images - //! support only CV_32FC1 type + // support only CV_32FC1 type CV_EXPORTS void convolve(const oclMat &image, const oclMat &temp1, oclMat &result); CV_EXPORTS void cvtColor(const oclMat &src, oclMat &dst, int code , int dcn = 0); CV_EXPORTS void setIdentity(oclMat& src, double val); + //////////////////////////////// Filter Engine //////////////////////////////// /*! @@ -982,7 +977,7 @@ namespace cv // real to complex dft requires at least v1.8 clAmdFft // real to complex dft output is not the same with cpu version // real to complex and complex to real does not support DFT_ROWS - CV_EXPORTS void dft(const oclMat &src, oclMat &dst, Size dft_size = Size(0, 0), int flags = 0); + CV_EXPORTS void dft(const oclMat &src, oclMat &dst, Size dft_size = Size(), int flags = 0); //! implements generalized matrix product algorithm GEMM from BLAS // The functionality requires clAmdBlas library diff --git a/modules/ocl/src/matrix_operations.cpp b/modules/ocl/src/matrix_operations.cpp index ff52b8a55b..78d1cd4afb 100644 --- a/modules/ocl/src/matrix_operations.cpp +++ b/modules/ocl/src/matrix_operations.cpp @@ -347,19 +347,14 @@ static void copy_to_with_mask(const oclMat &src, oclMat &dst, const oclMat &mask localThreads, args, -1, -1, compile_option); } -void cv::ocl::oclMat::copyTo( oclMat &m ) const -{ - CV_DbgAssert(!this->empty()); - m.create(size(), type()); - openCLCopyBuffer2D(clCxt, m.data, m.step, m.offset, - data, step, cols * elemSize(), rows, offset); -} - void cv::ocl::oclMat::copyTo( oclMat &mat, const oclMat &mask) const { if (mask.empty()) { - copyTo(mat); + CV_DbgAssert(!this->empty()); + mat.create(size(), type()); + openCLCopyBuffer2D(clCxt, mat.data, mat.step, mat.offset, + data, step, cols * elemSize(), rows, offset); } else { From f65286d3c613e43872709804431652182f4599a3 Mon Sep 17 00:00:00 2001 From: perping Date: Thu, 26 Sep 2013 14:04:11 +0800 Subject: [PATCH 59/68] Update the OpenCL documents. --- ...mera_calibration_and_3D_reconstruction.rst | 334 ++++++++++ .../doc/feature_detection_and_description.rst | 24 +- modules/ocl/doc/image_filtering.rst | 435 ++++++++++++- modules/ocl/doc/image_processing.rst | 206 +++---- .../doc/images/adaptiveBilateralFilter.jpg | Bin 0 -> 65410 bytes modules/ocl/doc/matrix_reductions.rst | 16 +- modules/ocl/doc/ml_machine_learning.rst | 88 +++ modules/ocl/doc/object_detection.rst | 31 +- modules/ocl/doc/ocl.rst | 3 + modules/ocl/doc/operations_on_matrices.rst | 50 +- .../doc/structures_and_utility_functions.rst | 4 +- modules/ocl/doc/video_analysis.rst | 570 ++++++++++++++++++ 12 files changed, 1588 insertions(+), 173 deletions(-) create mode 100644 modules/ocl/doc/camera_calibration_and_3D_reconstruction.rst create mode 100644 modules/ocl/doc/images/adaptiveBilateralFilter.jpg create mode 100644 modules/ocl/doc/ml_machine_learning.rst create mode 100644 modules/ocl/doc/video_analysis.rst diff --git a/modules/ocl/doc/camera_calibration_and_3D_reconstruction.rst b/modules/ocl/doc/camera_calibration_and_3D_reconstruction.rst new file mode 100644 index 0000000000..96ed6bbad3 --- /dev/null +++ b/modules/ocl/doc/camera_calibration_and_3D_reconstruction.rst @@ -0,0 +1,334 @@ +Camera Calibration and 3D Reconstruction +======================================== + +.. highlight:: cpp + + + +ocl::StereoBM_OCL +--------------------- +.. ocv:class:: ocl::StereoBM_OCL + +Class computing stereo correspondence (disparity map) using the block matching algorithm. :: + + class CV_EXPORTS StereoBM_OCL + { + public: + enum { BASIC_PRESET = 0, PREFILTER_XSOBEL = 1 }; + + enum { DEFAULT_NDISP = 64, DEFAULT_WINSZ = 19 }; + + //! the default constructor + StereoBM_OCL(); + //! the full constructor taking the camera-specific preset, number of disparities and the SAD window size. ndisparities must be multiple of 8. + StereoBM_OCL(int preset, int ndisparities = DEFAULT_NDISP, int winSize = DEFAULT_WINSZ); + + //! the stereo correspondence operator. Finds the disparity for the specified rectified stereo pair + //! Output disparity has CV_8U type. + void operator() ( const oclMat &left, const oclMat &right, oclMat &disparity); + + //! Some heuristics that tries to estmate + // if current GPU will be faster then CPU in this algorithm. + // It queries current active device. + static bool checkIfGpuCallReasonable(); + + int preset; + int ndisp; + int winSize; + + // If avergeTexThreshold == 0 => post procesing is disabled + // If avergeTexThreshold != 0 then disparity is set 0 in each point (x,y) where for left image + // SumOfHorizontalGradiensInWindow(x, y, winSize) < (winSize * winSize) * avergeTexThreshold + // i.e. input left image is low textured. + float avergeTexThreshold; + private: + /* hidden */ + }; + + +The class also performs pre- and post-filtering steps: Sobel pre-filtering (if ``PREFILTER_XSOBEL`` flag is set) and low textureness filtering (if ``averageTexThreshols > 0`` ). If ``avergeTexThreshold = 0`` , low textureness filtering is disabled. Otherwise, the disparity is set to 0 in each point ``(x, y)`` , where for the left image + +.. math:: + \sum HorizontalGradiensInWindow(x, y, winSize) < (winSize \cdot winSize) \cdot avergeTexThreshold + +This means that the input left image is low textured. + + +ocl::StereoBM_OCL::StereoBM_OCL +----------------------------------- +Enables :ocv:class:`ocl::StereoBM_OCL` constructors. + +.. ocv:function:: ocl::StereoBM_OCL::StereoBM_OCL() + +.. ocv:function:: ocl::StereoBM_OCL::StereoBM_OCL(int preset, int ndisparities = DEFAULT_NDISP, int winSize = DEFAULT_WINSZ) + + :param preset: Parameter presetting: + + * **BASIC_PRESET** Basic mode without pre-processing. + + * **PREFILTER_XSOBEL** Sobel pre-filtering mode. + + :param ndisparities: Number of disparities. It must be a multiple of 8 and less or equal to 256. + + :param winSize: Block size. + + + +ocl::StereoBM_OCL::operator () +---------------------------------- +Enables the stereo correspondence operator that finds the disparity for the specified rectified stereo pair. + +.. ocv:function:: void ocl::StereoBM_OCL::operator ()(const oclMat& left, const oclMat& right, oclMat& disparity) + + :param left: Left image. Only ``CV_8UC1`` type is supported. + + :param right: Right image with the same size and the same type as the left one. + + :param disparity: Output disparity map. It is a ``CV_8UC1`` image with the same size as the input images. + + :param stream: Stream for the asynchronous version. + + +ocl::StereoBM_OCL::checkIfGpuCallReasonable +----------------------------------------------- +Uses a heuristic method to estimate whether the current GPU is faster than the CPU in this algorithm. It queries the currently active device. + +.. ocv:function:: bool ocl::StereoBM_OCL::checkIfGpuCallReasonable() + +ocl::StereoBeliefPropagation +-------------------------------- +.. ocv:class:: ocl::StereoBeliefPropagation + +Class computing stereo correspondence using the belief propagation algorithm. :: + + class CV_EXPORTS StereoBeliefPropagation + { + public: + enum { DEFAULT_NDISP = 64 }; + enum { DEFAULT_ITERS = 5 }; + enum { DEFAULT_LEVELS = 5 }; + static void estimateRecommendedParams(int width, int height, int &ndisp, int &iters, int &levels); + explicit StereoBeliefPropagation(int ndisp = DEFAULT_NDISP, + int iters = DEFAULT_ITERS, + int levels = DEFAULT_LEVELS, + int msg_type = CV_16S); + StereoBeliefPropagation(int ndisp, int iters, int levels, + float max_data_term, float data_weight, + float max_disc_term, float disc_single_jump, + int msg_type = CV_32F); + void operator()(const oclMat &left, const oclMat &right, oclMat &disparity); + void operator()(const oclMat &data, oclMat &disparity); + int ndisp; + int iters; + int levels; + float max_data_term; + float data_weight; + float max_disc_term; + float disc_single_jump; + int msg_type; + private: + /* hidden */ + }; + +The class implements algorithm described in [Felzenszwalb2006]_ . It can compute own data cost (using a truncated linear model) or use a user-provided data cost. + +.. note:: + + ``StereoBeliefPropagation`` requires a lot of memory for message storage: + + .. math:: + + width \_ step \cdot height \cdot ndisp \cdot 4 \cdot (1 + 0.25) + + and for data cost storage: + + .. math:: + + width\_step \cdot height \cdot ndisp \cdot (1 + 0.25 + 0.0625 + \dotsm + \frac{1}{4^{levels}}) + + ``width_step`` is the number of bytes in a line including padding. + + + +ocl::StereoBeliefPropagation::StereoBeliefPropagation +--------------------------------------------------------- +Enables the :ocv:class:`ocl::StereoBeliefPropagation` constructors. + +.. ocv:function:: ocl::StereoBeliefPropagation::StereoBeliefPropagation(int ndisp = DEFAULT_NDISP, int iters = DEFAULT_ITERS, int levels = DEFAULT_LEVELS, int msg_type = CV_16S) + +.. ocv:function:: ocl::StereoBeliefPropagation::StereoBeliefPropagation(int ndisp, int iters, int levels, float max_data_term, float data_weight, float max_disc_term, float disc_single_jump, int msg_type = CV_32F) + + :param ndisp: Number of disparities. + + :param iters: Number of BP iterations on each level. + + :param levels: Number of levels. + + :param max_data_term: Threshold for data cost truncation. + + :param data_weight: Data weight. + + :param max_disc_term: Threshold for discontinuity truncation. + + :param disc_single_jump: Discontinuity single jump. + + :param msg_type: Type for messages. ``CV_16SC1`` and ``CV_32FC1`` types are supported. + +``StereoBeliefPropagation`` uses a truncated linear model for the data cost and discontinuity terms: + +.. math:: + + DataCost = data \_ weight \cdot \min ( \lvert Img_Left(x,y)-Img_Right(x-d,y) \rvert , max \_ data \_ term) + +.. math:: + + DiscTerm = \min (disc \_ single \_ jump \cdot \lvert f_1-f_2 \rvert , max \_ disc \_ term) + +For more details, see [Felzenszwalb2006]_. + +By default, :ocv:class:`ocl::StereoBeliefPropagation` uses floating-point arithmetics and the ``CV_32FC1`` type for messages. But it can also use fixed-point arithmetics and the ``CV_16SC1`` message type for better performance. To avoid an overflow in this case, the parameters must satisfy the following requirement: + +.. math:: + + 10 \cdot 2^{levels-1} \cdot max \_ data \_ term < SHRT \_ MAX + + + +ocl::StereoBeliefPropagation::estimateRecommendedParams +----------------------------------------------------------- +Uses a heuristic method to compute the recommended parameters ( ``ndisp``, ``iters`` and ``levels`` ) for the specified image size ( ``width`` and ``height`` ). + +.. ocv:function:: void ocl::StereoBeliefPropagation::estimateRecommendedParams(int width, int height, int& ndisp, int& iters, int& levels) + + + +ocl::StereoBeliefPropagation::operator () +--------------------------------------------- +Enables the stereo correspondence operator that finds the disparity for the specified rectified stereo pair or data cost. + +.. ocv:function:: void ocl::StereoBeliefPropagation::operator ()(const oclMat& left, const oclMat& right, oclMat& disparity) + +.. ocv:function:: void ocl::StereoBeliefPropagation::operator ()(const oclMat& data, oclMat& disparity) + + :param left: Left image. ``CV_8UC1`` , ``CV_8UC3`` and ``CV_8UC4`` types are supported. + + :param right: Right image with the same size and the same type as the left one. + + :param data: User-specified data cost, a matrix of ``msg_type`` type and ``Size(*ndisp, )`` size. + + :param disparity: Output disparity map. If ``disparity`` is empty, the output type is ``CV_16SC1`` . Otherwise, the type is retained. + + :param stream: Stream for the asynchronous version. + +ocl::StereoConstantSpaceBP +------------------------------ +.. ocv:class:: ocl::StereoConstantSpaceBP + +Class computing stereo correspondence using the constant space belief propagation algorithm. :: + + class CV_EXPORTS StereoConstantSpaceBP + { + public: + enum { DEFAULT_NDISP = 128 }; + enum { DEFAULT_ITERS = 8 }; + enum { DEFAULT_LEVELS = 4 }; + enum { DEFAULT_NR_PLANE = 4 }; + static void estimateRecommendedParams(int width, int height, int &ndisp, int &iters, int &levels, int &nr_plane); + explicit StereoConstantSpaceBP( + int ndisp = DEFAULT_NDISP, + int iters = DEFAULT_ITERS, + int levels = DEFAULT_LEVELS, + int nr_plane = DEFAULT_NR_PLANE, + int msg_type = CV_32F); + StereoConstantSpaceBP(int ndisp, int iters, int levels, int nr_plane, + float max_data_term, float data_weight, float max_disc_term, float disc_single_jump, + int min_disp_th = 0, + int msg_type = CV_32F); + void operator()(const oclMat &left, const oclMat &right, oclMat &disparity); + int ndisp; + int iters; + int levels; + int nr_plane; + float max_data_term; + float data_weight; + float max_disc_term; + float disc_single_jump; + int min_disp_th; + int msg_type; + bool use_local_init_data_cost; + private: + /* hidden */ + }; + +The class implements algorithm described in [Yang2010]_. ``StereoConstantSpaceBP`` supports both local minimum and global minimum data cost initialization algorithms. For more details, see the paper mentioned above. By default, a local algorithm is used. To enable a global algorithm, set ``use_local_init_data_cost`` to ``false`` . + + +ocl::StereoConstantSpaceBP::StereoConstantSpaceBP +----------------------------------------------------- +Enables the :ocv:class:`ocl::StereoConstantSpaceBP` constructors. + +.. ocv:function:: ocl::StereoConstantSpaceBP::StereoConstantSpaceBP(int ndisp = DEFAULT_NDISP, int iters = DEFAULT_ITERS, int levels = DEFAULT_LEVELS, int nr_plane = DEFAULT_NR_PLANE, int msg_type = CV_32F) + +.. ocv:function:: ocl::StereoConstantSpaceBP::StereoConstantSpaceBP(int ndisp, int iters, int levels, int nr_plane, float max_data_term, float data_weight, float max_disc_term, float disc_single_jump, int min_disp_th = 0, int msg_type = CV_32F) + + :param ndisp: Number of disparities. + + :param iters: Number of BP iterations on each level. + + :param levels: Number of levels. + + :param nr_plane: Number of disparity levels on the first level. + + :param max_data_term: Truncation of data cost. + + :param data_weight: Data weight. + + :param max_disc_term: Truncation of discontinuity. + + :param disc_single_jump: Discontinuity single jump. + + :param min_disp_th: Minimal disparity threshold. + + :param msg_type: Type for messages. ``CV_16SC1`` and ``CV_32FC1`` types are supported. + +``StereoConstantSpaceBP`` uses a truncated linear model for the data cost and discontinuity terms: + +.. math:: + + DataCost = data \_ weight \cdot \min ( \lvert I_2-I_1 \rvert , max \_ data \_ term) + +.. math:: + + DiscTerm = \min (disc \_ single \_ jump \cdot \lvert f_1-f_2 \rvert , max \_ disc \_ term) + +For more details, see [Yang2010]_. + +By default, ``StereoConstantSpaceBP`` uses floating-point arithmetics and the ``CV_32FC1`` type for messages. But it can also use fixed-point arithmetics and the ``CV_16SC1`` message type for better performance. To avoid an overflow in this case, the parameters must satisfy the following requirement: + +.. math:: + + 10 \cdot 2^{levels-1} \cdot max \_ data \_ term < SHRT \_ MAX + + + +ocl::StereoConstantSpaceBP::estimateRecommendedParams +--------------------------------------------------------- +Uses a heuristic method to compute parameters (ndisp, iters, levelsand nrplane) for the specified image size (widthand height). + +.. ocv:function:: void ocl::StereoConstantSpaceBP::estimateRecommendedParams(int width, int height, int& ndisp, int& iters, int& levels, int& nr_plane) + + + +ocl::StereoConstantSpaceBP::operator () +------------------------------------------- +Enables the stereo correspondence operator that finds the disparity for the specified rectified stereo pair. + +.. ocv:function:: void ocl::StereoConstantSpaceBP::operator ()(const oclMat& left, const oclMat& right, oclMat& disparity) + + :param left: Left image. ``CV_8UC1`` , ``CV_8UC3`` and ``CV_8UC4`` types are supported. + + :param right: Right image with the same size and the same type as the left one. + + :param disparity: Output disparity map. If ``disparity`` is empty, the output type is ``CV_16SC1`` . Otherwise, the output type is ``disparity.type()`` . + + :param stream: Stream for the asynchronous version. \ No newline at end of file diff --git a/modules/ocl/doc/feature_detection_and_description.rst b/modules/ocl/doc/feature_detection_and_description.rst index 11fb27242c..d052ef8cc4 100644 --- a/modules/ocl/doc/feature_detection_and_description.rst +++ b/modules/ocl/doc/feature_detection_and_description.rst @@ -37,7 +37,7 @@ Finds edges in an image using the [Canny86]_ algorithm. ocl::BruteForceMatcher_OCL_base -------------------------------- +----------------------------------- .. ocv:class:: ocl::BruteForceMatcher_OCL_base Brute-force descriptor matcher. For each descriptor in the first set, this matcher finds the closest descriptor in the second set by trying each one. This descriptor matcher supports masking permissible matches between descriptor sets. :: @@ -153,7 +153,7 @@ The class ``BruteForceMatcher_OCL_base`` has an interface similar to the class : ocl::BruteForceMatcher_OCL_base::match --------------------------------------- +------------------------------------------ Finds the best match for each descriptor from a query set with train descriptors. .. ocv:function:: void ocl::BruteForceMatcher_OCL_base::match(const oclMat& query, const oclMat& train, std::vector& matches, const oclMat& mask = oclMat()) @@ -169,14 +169,14 @@ Finds the best match for each descriptor from a query set with train descriptors ocl::BruteForceMatcher_OCL_base::makeGpuCollection --------------------------------------------------- +------------------------------------------------------ Performs a GPU collection of train descriptors and masks in a suitable format for the :ocv:func:`ocl::BruteForceMatcher_OCL_base::matchCollection` function. .. ocv:function:: void ocl::BruteForceMatcher_OCL_base::makeGpuCollection(oclMat& trainCollection, oclMat& maskCollection, const vector& masks = std::vector()) ocl::BruteForceMatcher_OCL_base::matchDownload ----------------------------------------------- +-------------------------------------------------- Downloads matrices obtained via :ocv:func:`ocl::BruteForceMatcher_OCL_base::matchSingle` or :ocv:func:`ocl::BruteForceMatcher_OCL_base::matchCollection` to vector with :ocv:class:`DMatch`. .. ocv:function:: static void ocl::BruteForceMatcher_OCL_base::matchDownload( const oclMat& trainIdx, const oclMat& distance, std::vector& matches ) @@ -185,7 +185,7 @@ Downloads matrices obtained via :ocv:func:`ocl::BruteForceMatcher_OCL_base::matc ocl::BruteForceMatcher_OCL_base::matchConvert ---------------------------------------------- +------------------------------------------------- Converts matrices obtained via :ocv:func:`ocl::BruteForceMatcher_OCL_base::matchSingle` or :ocv:func:`ocl::BruteForceMatcher_OCL_base::matchCollection` to vector with :ocv:class:`DMatch`. .. ocv:function:: void ocl::BruteForceMatcher_OCL_base::matchConvert(const Mat& trainIdx, const Mat& distance, std::vector&matches) @@ -195,7 +195,7 @@ Converts matrices obtained via :ocv:func:`ocl::BruteForceMatcher_OCL_base::match ocl::BruteForceMatcher_OCL_base::knnMatch ------------------------------------------ +--------------------------------------------- Finds the ``k`` best matches for each descriptor from a query set with train descriptors. .. ocv:function:: void ocl::BruteForceMatcher_OCL_base::knnMatch(const oclMat& query, const oclMat& train, std::vector< std::vector >&matches, int k, const oclMat& mask = oclMat(), bool compactResult = false) @@ -226,7 +226,7 @@ The third variant of the method stores the results in GPU memory. ocl::BruteForceMatcher_OCL_base::knnMatchDownload -------------------------------------------------- +----------------------------------------------------- Downloads matrices obtained via :ocv:func:`ocl::BruteForceMatcher_OCL_base::knnMatchSingle` or :ocv:func:`ocl::BruteForceMatcher_OCL_base::knnMatch2Collection` to vector with :ocv:class:`DMatch`. .. ocv:function:: void ocl::BruteForceMatcher_OCL_base::knnMatchDownload(const oclMat& trainIdx, const oclMat& distance, std::vector< std::vector >&matches, bool compactResult = false) @@ -238,7 +238,7 @@ If ``compactResult`` is ``true`` , the ``matches`` vector does not contain match ocl::BruteForceMatcher_OCL_base::knnMatchConvert ------------------------------------------------- +---------------------------------------------------- Converts matrices obtained via :ocv:func:`ocl::BruteForceMatcher_OCL_base::knnMatchSingle` or :ocv:func:`ocl::BruteForceMatcher_OCL_base::knnMatch2Collection` to CPU vector with :ocv:class:`DMatch`. .. ocv:function:: void ocl::BruteForceMatcher_OCL_base::knnMatchConvert(const Mat& trainIdx, const Mat& distance, std::vector< std::vector >&matches, bool compactResult = false) @@ -250,7 +250,7 @@ If ``compactResult`` is ``true`` , the ``matches`` vector does not contain match ocl::BruteForceMatcher_OCL_base::radiusMatch --------------------------------------------- +------------------------------------------------ For each query descriptor, finds the best matches with a distance less than a given threshold. .. ocv:function:: void ocl::BruteForceMatcher_OCL_base::radiusMatch(const oclMat& query, const oclMat& train, std::vector< std::vector >&matches, float maxDistance, const oclMat& mask = oclMat(), bool compactResult = false) @@ -283,7 +283,7 @@ The third variant of the method stores the results in GPU memory and does not st ocl::BruteForceMatcher_OCL_base::radiusMatchDownload ----------------------------------------------------- +-------------------------------------------------------- Downloads matrices obtained via :ocv:func:`ocl::BruteForceMatcher_OCL_base::radiusMatchSingle` or :ocv:func:`ocl::BruteForceMatcher_OCL_base::radiusMatchCollection` to vector with :ocv:class:`DMatch`. .. ocv:function:: void ocl::BruteForceMatcher_OCL_base::radiusMatchDownload(const oclMat& trainIdx, const oclMat& distance, const oclMat& nMatches, std::vector< std::vector >&matches, bool compactResult = false) @@ -296,7 +296,7 @@ If ``compactResult`` is ``true`` , the ``matches`` vector does not contain match ocl::BruteForceMatcher_OCL_base::radiusMatchConvert ---------------------------------------------------- +------------------------------------------------------- Converts matrices obtained via :ocv:func:`ocl::BruteForceMatcher_OCL_base::radiusMatchSingle` or :ocv:func:`ocl::BruteForceMatcher_OCL_base::radiusMatchCollection` to vector with :ocv:class:`DMatch`. .. ocv:function:: void ocl::BruteForceMatcher_OCL_base::radiusMatchConvert(const Mat& trainIdx, const Mat& distance, const Mat& nMatches, std::vector< std::vector >&matches, bool compactResult = false) @@ -306,7 +306,7 @@ Converts matrices obtained via :ocv:func:`ocl::BruteForceMatcher_OCL_base::radiu If ``compactResult`` is ``true`` , the ``matches`` vector does not contain matches for fully masked-out query descriptors. ocl::HOGDescriptor ------------------- +---------------------- .. ocv:struct:: ocl::HOGDescriptor diff --git a/modules/ocl/doc/image_filtering.rst b/modules/ocl/doc/image_filtering.rst index 1f90eeddab..9f18c8e47d 100644 --- a/modules/ocl/doc/image_filtering.rst +++ b/modules/ocl/doc/image_filtering.rst @@ -3,6 +3,360 @@ Image Filtering .. highlight:: cpp +ocl::BaseRowFilter_GPU +-------------------------- +.. ocv:class:: ocl::BaseRowFilter_GPU + +Base class for linear or non-linear filters that processes rows of 2D arrays. Such filters are used for the "horizontal" filtering passes in separable filters. :: + + class CV_EXPORTS BaseRowFilter_GPU + { + public: + BaseRowFilter_GPU(int ksize_, int anchor_, int bordertype_) : ksize(ksize_), anchor(anchor_), bordertype(bordertype_) {} + virtual ~BaseRowFilter_GPU() {} + virtual void operator()(const oclMat &src, oclMat &dst) = 0; + int ksize, anchor, bordertype; + }; + +.. note:: This class does not allocate memory for a destination image. Usually this class is used inside :ocv:class:`ocl::FilterEngine_GPU`. + +ocl::BaseColumnFilter_GPU +----------------------------- +.. ocv:class:: ocl::BaseColumnFilter_GPU + +Base class for linear or non-linear filters that processes columns of 2D arrays. Such filters are used for the "vertical" filtering passes in separable filters. :: + + class CV_EXPORTS BaseColumnFilter_GPU + { + public: + BaseColumnFilter_GPU(int ksize_, int anchor_, int bordertype_) : ksize(ksize_), anchor(anchor_), bordertype(bordertype_) {} + virtual ~BaseColumnFilter_GPU() {} + virtual void operator()(const oclMat &src, oclMat &dst) = 0; + int ksize, anchor, bordertype; + }; + +.. note:: This class does not allocate memory for a destination image. Usually this class is used inside :ocv:class:`ocl::FilterEngine_GPU`. + +ocl::BaseFilter_GPU +----------------------- +.. ocv:class:: ocl::BaseFilter_GPU + +Base class for non-separable 2D filters. :: + + class CV_EXPORTS BaseFilter_GPU + { + public: + BaseFilter_GPU(const Size &ksize_, const Point &anchor_, const int &borderType_) + : ksize(ksize_), anchor(anchor_), borderType(borderType_) {} + virtual ~BaseFilter_GPU() {} + virtual void operator()(const oclMat &src, oclMat &dst) = 0; + Size ksize; + Point anchor; + int borderType; + }; + +.. note:: This class does not allocate memory for a destination image. Usually this class is used inside :ocv:class:`ocl::FilterEngine_GPU` + +ocl::FilterEngine_GPU +------------------------ +.. ocv:class:: ocl::FilterEngine_GPU + +Base class for the Filter Engine. :: + + class CV_EXPORTS FilterEngine_GPU + { + public: + virtual ~FilterEngine_GPU() {} + + virtual void apply(const oclMat &src, oclMat &dst, Rect roi = Rect(0, 0, -1, -1)) = 0; + }; + +The class can be used to apply an arbitrary filtering operation to an image. It contains all the necessary intermediate buffers. Pointers to the initialized ``FilterEngine_GPU`` instances are returned by various ``create*Filter_GPU`` functions (see below), and they are used inside high-level functions such as :ocv:func:`ocl::filter2D`, :ocv:func:`ocl::erode`, :ocv:func:`ocl::Sobel` , and others. + +By using ``FilterEngine_GPU`` instead of functions you can avoid unnecessary memory allocation for intermediate buffers and get better performance: :: + + while (...) + { + ocl::oclMat src = getImg(); + ocl::oclMat dst; + // Allocate and release buffers at each iterations + ocl::GaussianBlur(src, dst, ksize, sigma1); + } + + // Allocate buffers only once + cv::Ptr filter = + ocl::createGaussianFilter_GPU(CV_8UC4, ksize, sigma1); + while (...) + { + ocl::oclMat src = getImg(); + ocl::oclMat dst; + filter->apply(src, dst, cv::Rect(0, 0, src.cols, src.rows)); + } + // Release buffers only once + filter.release(); + + +``FilterEngine_GPU`` can process a rectangular sub-region of an image. By default, if ``roi == Rect(0,0,-1,-1)`` , ``FilterEngine_GPU`` processes the inner region of an image ( ``Rect(anchor.x, anchor.y, src_size.width - ksize.width, src_size.height - ksize.height)`` ) because some filters do not check whether indices are outside the image for better performance. See below to understand which filters support processing the whole image and which do not and identify image type limitations. + +.. note:: The GPU filters do not support the in-place mode. + +.. seealso:: :ocv:class:`ocl::BaseRowFilter_GPU`, :ocv:class:`ocl::BaseColumnFilter_GPU`, :ocv:class:`ocl::BaseFilter_GPU`, :ocv:func:`ocl::createFilter2D_GPU`, :ocv:func:`ocl::createSeparableFilter_GPU`, :ocv:func:`ocl::createBoxFilter_GPU`, :ocv:func:`ocl::createMorphologyFilter_GPU`, :ocv:func:`ocl::createLinearFilter_GPU`, :ocv:func:`ocl::createSeparableLinearFilter_GPU`, :ocv:func:`ocl::createDerivFilter_GPU`, :ocv:func:`ocl::createGaussianFilter_GPU` + +ocl::createFilter2D_GPU +--------------------------- +Creates a non-separable filter engine with the specified filter. + +.. ocv:function:: Ptr ocl::createFilter2D_GPU( const Ptr filter2D) + + :param filter2D: Non-separable 2D filter. + +Usually this function is used inside such high-level functions as :ocv:func:`ocl::createLinearFilter_GPU`, :ocv:func:`ocl::createBoxFilter_GPU`. + + +ocl::createSeparableFilter_GPU +---------------------------------- +Creates a separable filter engine with the specified filters. + +.. ocv:function:: Ptr ocl::createSeparableFilter_GPU(const Ptr &rowFilter, const Ptr &columnFilter) + + :param rowFilter: "Horizontal" 1D filter. + + :param columnFilter: "Vertical" 1D filter. + +Usually this function is used inside such high-level functions as :ocv:func:`ocl::createSeparableLinearFilter_GPU`. + +ocl::createBoxFilter_GPU +---------------------------- +Creates a normalized 2D box filter. + +.. ocv:function:: Ptr ocl::createBoxFilter_GPU(int srcType, int dstType, const Size &ksize, const Point &anchor = Point(-1, -1), int borderType = BORDER_DEFAULT) + +.. ocv:function:: Ptr ocl::getBoxFilter_GPU(int srcType, int dstType, const Size &ksize, Point anchor = Point(-1, -1), int borderType = BORDER_DEFAULT) + + :param srcType: Input image type supporting ``CV_8UC1`` and ``CV_8UC4`` . + + :param dstType: Output image type. It supports only the same values as the source type. + + :param ksize: Kernel size. + + :param anchor: Anchor point. The default value ``Point(-1, -1)`` means that the anchor is at the kernel center. + + :param borderType: Supports border type: BORDER_CONSTANT, BORDER_REPLICATE, BORDER_REFLECT,BORDER_REFLECT_101,BORDER_WRAP. + +.. note:: This filter does not check out-of-border accesses, so only a proper sub-matrix of a bigger matrix has to be passed to it. + +.. seealso:: :ocv:func:`boxFilter` + +ocl::boxFilter +------------------ +Smooths the image using the normalized box filter. + +.. ocv:function:: void ocl::boxFilter(const oclMat &src, oclMat &dst, int ddepth, Size ksize, Point anchor = Point(-1, -1), int borderType = BORDER_DEFAULT) + + :param src: Input image. ``CV_8UC1`` and ``CV_8UC4`` source types are supported. + + :param dst: Output image type. The size and type is the same as ``src`` . + + :param ddepth: Output image depth. If -1, the output image has the same depth as the input one. The only values allowed here are ``CV_8U`` and -1. + + :param ksize: Kernel size. + + :param anchor: Anchor point. The default value ``Point(-1, -1)`` means that the anchor is at the kernel center. + + :param borderType: Supports border type: BORDER_CONSTANT, BORDER_REPLICATE, BORDER_REFLECT,BORDER_REFLECT_101,BORDER_WRAP. + +Smoothes image using box filter.Supports data type: CV_8UC1, CV_8UC4, CV_32FC1 and CV_32FC4. + +.. note:: This filter does not check out-of-border accesses, so only a proper sub-matrix of a bigger matrix has to be passed to it. + +ocl::blur +------------- +Acts as a synonym for the normalized box filter. + +.. ocv:function:: void ocl::blur(const oclMat &src, oclMat &dst, Size ksize, Point anchor = Point(-1, -1), int borderType = BORDER_CONSTANT) + + :param src: Input image. ``CV_8UC1`` and ``CV_8UC4`` source types are supported. + + :param dst: Output image type with the same size and type as ``src`` . + + :param ksize: Kernel size. + + :param anchor: Anchor point. The default value Point(-1, -1) means that the anchor is at the kernel center. + + :param borderType: Supports border type: BORDER_CONSTANT, BORDER_REPLICATE, BORDER_REFLECT,BORDER_REFLECT_101,BORDER_WRAP. + +.. note:: This filter does not check out-of-border accesses, so only a proper sub-matrix of a bigger matrix has to be passed to it. + +.. seealso:: :ocv:func:`blur`, :ocv:func:`ocl::boxFilter` + +ocl::createMorphologyFilter_GPU +----------------------------------- +Creates a 2D morphological filter. + +.. ocv:function:: Ptr ocl::createMorphologyFilter_GPU(int op, int type, const Mat &kernel, const Point &anchor = Point(-1, -1), int iterations = 1) + +.. ocv:function:: Ptr ocl::getMorphologyFilter_GPU(int op, int type, const Mat &kernel, const Size &ksize, Point anchor = Point(-1, -1)) + + :param op: Morphology operation id. Only ``MORPH_ERODE`` and ``MORPH_DILATE`` are supported. + + :param type: Input/output image type. Only ``CV_8UC1`` and ``CV_8UC4`` are supported. + + :param kernel: 2D 8-bit structuring element for the morphological operation. + + :param ksize: Size of a horizontal or vertical structuring element used for separable morphological operations. + + :param anchor: Anchor position within the structuring element. Negative values mean that the anchor is at the center. + +.. note:: This filter does not check out-of-border accesses, so only a proper sub-matrix of a bigger matrix has to be passed to it. + +.. seealso:: :ocv:func:`createMorphologyFilter` + +ocl::createLinearFilter_GPU +------------------------------- +Creates a non-separable linear filter. + +.. ocv:function:: Ptr ocl::createLinearFilter_GPU(int srcType, int dstType, const Mat &kernel, const Point &anchor = Point(-1, -1), int borderType = BORDER_DEFAULT) + + :param srcType: Input image type. Supports ``CV_8U`` , ``CV_16U`` and ``CV_32F`` one and four channel image. + + :param dstType: Output image type. The same type as ``src`` is supported. + + :param kernel: 2D array of filter coefficients. Floating-point coefficients will be converted to fixed-point representation before the actual processing. Supports size up to 16. For larger kernels use :ocv:func:`ocl::convolve`. + + :param anchor: Anchor point. The default value Point(-1, -1) means that the anchor is at the kernel center. + + :param borderType: Pixel extrapolation method. For details, see :ocv:func:`borderInterpolate` . + +.. seealso:: :ocv:func:`createLinearFilter` + + +ocl::filter2D +----------------- +Applies the non-separable 2D linear filter to an image. + +.. ocv:function:: void ocl::filter2D(const oclMat &src, oclMat &dst, int ddepth, const Mat &kernel, Point anchor = Point(-1, -1), int borderType = BORDER_DEFAULT) + + :param src: Source image. Supports ``CV_8U`` , ``CV_16U`` and ``CV_32F`` one and four channel image. + + :param dst: Destination image. The size and the number of channels is the same as ``src`` . + + :param ddepth: Desired depth of the destination image. If it is negative, it is the same as ``src.depth()`` . It supports only the same depth as the source image depth. + + :param kernel: 2D array of filter coefficients. + + :param anchor: Anchor of the kernel that indicates the relative position of a filtered point within the kernel. The anchor resides within the kernel. The special default value (-1,-1) means that the anchor is at the kernel center. + + :param borderType: Pixel extrapolation method. For details, see :ocv:func:`borderInterpolate` . + + :param stream: Stream for the asynchronous version. + +ocl::getLinearRowFilter_GPU +------------------------------- +Creates a primitive row filter with the specified kernel. + +.. ocv:function:: Ptr ocl::getLinearRowFilter_GPU(int srcType, int bufType, const Mat &rowKernel, int anchor = -1, int bordertype = BORDER_DEFAULT) + + :param srcType: Source array type. Only ``CV_8UC1`` , ``CV_8UC4`` , ``CV_16SC1`` , ``CV_16SC2`` , ``CV_16SC3`` , ``CV_32SC1`` , ``CV_32FC1`` source types are supported. + + :param bufType: Intermediate buffer type with as many channels as ``srcType`` . + + :param rowKernel: Filter coefficients. Support kernels with ``size <= 16`` . + + :param anchor: Anchor position within the kernel. Negative values mean that the anchor is positioned at the aperture center. + + :param borderType: Pixel extrapolation method. For details, see :ocv:func:`borderInterpolate`. + +.. seealso:: :ocv:func:`createSeparableLinearFilter` . + + +ocl::getLinearColumnFilter_GPU +---------------------------------- +Creates a primitive column filter with the specified kernel. + +.. ocv:function:: Ptr ocl::getLinearColumnFilter_GPU(int bufType, int dstType, const Mat &columnKernel, int anchor = -1, int bordertype = BORDER_DEFAULT, double delta = 0.0) + + :param bufType: Intermediate buffer type with as many channels as ``dstType`` . + + :param dstType: Destination array type. ``CV_8UC1`` , ``CV_8UC4`` , ``CV_16SC1`` , ``CV_16SC2`` , ``CV_16SC3`` , ``CV_32SC1`` , ``CV_32FC1`` destination types are supported. + + :param columnKernel: Filter coefficients. Support kernels with ``size <= 16`` . + + :param anchor: Anchor position within the kernel. Negative values mean that the anchor is positioned at the aperture center. + + :param bordertype: Pixel extrapolation method. For details, see :ocv:func:`borderInterpolate` . + + :param delta: default value is 0.0. + +.. seealso:: :ocv:func:`ocl::getLinearRowFilter_GPU`, :ocv:func:`createSeparableLinearFilter` + +ocl::createSeparableLinearFilter_GPU +---------------------------------------- +Creates a separable linear filter engine. + +.. ocv:function:: Ptr ocl::createSeparableLinearFilter_GPU(int srcType, int dstType, const Mat &rowKernel, const Mat &columnKernel, const Point &anchor = Point(-1, -1), double delta = 0.0, int bordertype = BORDER_DEFAULT) + + :param srcType: Source array type. ``CV_8UC1`` , ``CV_8UC4`` , ``CV_16SC1`` , ``CV_16SC2`` , ``CV_16SC3`` , ``CV_32SC1`` , ``CV_32FC1`` source types are supported. + + :param dstType: Destination array type. ``CV_8UC1`` , ``CV_8UC4`` , ``CV_16SC1`` , ``CV_16SC2`` , ``CV_16SC3`` , ``CV_32SC1`` , ``CV_32FC1`` destination types are supported. + + :param rowKernel: Horizontal filter coefficients. Support kernels with ``size <= 16`` . + + :param columnKernel: Vertical filter coefficients. Support kernels with ``size <= 16`` . + + :param anchor: Anchor position within the kernel. Negative values mean that anchor is positioned at the aperture center. + + :param delta: default value is 0.0. + + :param bordertype: Pixel extrapolation method. + +.. seealso:: :ocv:func:`ocl::getLinearRowFilter_GPU`, :ocv:func:`ocl::getLinearColumnFilter_GPU`, :ocv:func:`createSeparableLinearFilter` + + +ocl::sepFilter2D +-------------------- +Applies a separable 2D linear filter to an image. + +.. ocv:function:: void ocl::sepFilter2D(const oclMat &src, oclMat &dst, int ddepth, const Mat &kernelX, const Mat &kernelY, Point anchor = Point(-1, -1), double delta = 0.0, int bordertype = BORDER_DEFAULT) + + :param src: Source image. ``CV_8UC1`` , ``CV_8UC4`` , ``CV_16SC1`` , ``CV_16SC2`` , ``CV_32SC1`` , ``CV_32FC1`` source types are supported. + + :param dst: Destination image with the same size and number of channels as ``src`` . + + :param ddepth: Destination image depth. ``CV_8U`` , ``CV_16S`` , ``CV_32S`` , and ``CV_32F`` are supported. + + :param kernelX: Horizontal filter coefficients. + + :param kernelY: Vertical filter coefficients. + + :param anchor: Anchor position within the kernel. The default value ``(-1, 1)`` means that the anchor is at the kernel center. + + :param delta: default value is 0.0. + + :param bordertype: Pixel extrapolation method. For details, see :ocv:func:`borderInterpolate`. + +.. seealso:: :ocv:func:`ocl::createSeparableLinearFilter_GPU`, :ocv:func:`sepFilter2D` + +ocl::createDerivFilter_GPU +------------------------------ +Creates a filter engine for the generalized Sobel operator. + +.. ocv:function:: Ptr ocl::createDerivFilter_GPU( int srcType, int dstType, int dx, int dy, int ksize, int borderType = BORDER_DEFAULT ) + + :param srcType: Source image type. ``CV_8UC1`` , ``CV_8UC4`` , ``CV_16SC1`` , ``CV_16SC2`` , ``CV_16SC3`` , ``CV_32SC1`` , ``CV_32FC1`` source types are supported. + + :param dstType: Destination image type with as many channels as ``srcType`` , ``CV_8U`` , ``CV_16S`` , ``CV_32S`` , and ``CV_32F`` depths are supported. + + :param dx: Derivative order in respect of x. + + :param dy: Derivative order in respect of y. + + :param ksize: Aperture size. See :ocv:func:`getDerivKernels` for details. + + :param borderType: Pixel extrapolation method. For details, see :ocv:func:`borderInterpolate`. + +.. seealso:: :ocv:func:`ocl::createSeparableLinearFilter_GPU`, :ocv:func:`createDerivFilter` + + ocl::Sobel ------------------ Returns void @@ -53,43 +407,41 @@ Returns void The function computes the first x- or y- spatial image derivative using Scharr operator. Surpport 8UC1 8UC4 32SC1 32SC4 32FC1 32FC4 data type. -ocl::GaussianBlur ------------------- -Returns void +ocl::createGaussianFilter_GPU +--------------------------------- +Creates a Gaussian filter engine. -.. ocv:function:: void ocl::GaussianBlur(const oclMat &src, oclMat &dst, Size ksize, double sigma1, double sigma2 = 0, int bordertype = BORDER_DEFAULT) +.. ocv:function:: Ptr ocl::createGaussianFilter_GPU(int type, Size ksize, double sigma1, double sigma2 = 0, int bordertype = BORDER_DEFAULT) - :param src: The source image + :param type: Source and destination image type. ``CV_8UC1`` , ``CV_8UC4`` , ``CV_16SC1`` , ``CV_16SC2`` , ``CV_16SC3`` , ``CV_32SC1`` , ``CV_32FC1`` are supported. - :param dst: The destination image; It will have the same size and the same type as src + :param ksize: Aperture size. See :ocv:func:`getGaussianKernel` for details. - :param ksize: The Gaussian kernel size; ksize.width and ksize.height can differ, but they both must be positive and odd. Or, they can be zero's, then they are computed from sigma + :param sigma1: Gaussian sigma in the horizontal direction. See :ocv:func:`getGaussianKernel` for details. - :param sigma1sigma2: The Gaussian kernel standard deviations in X and Y direction. If sigmaY is zero, it is set to be equal to sigmaX. If they are both zeros, they are computed from ksize.width and ksize.height. To fully control the result regardless of possible future modification of all this semantics, it is recommended to specify all of ksize, sigmaX and sigmaY + :param sigma2: Gaussian sigma in the vertical direction. If 0, then :math:`\texttt{sigma2}\leftarrow\texttt{sigma1}` . - :param bordertype: Pixel extrapolation method. + :param bordertype: Pixel extrapolation method. For details, see :ocv:func:`borderInterpolate`. -The function convolves the source image with the specified Gaussian kernel. In-place filtering is supported. Surpport 8UC1 8UC4 32SC1 32SC4 32FC1 32FC4 data type. +.. seealso:: :ocv:func:`ocl::createSeparableLinearFilter_GPU`, :ocv:func:`createGaussianFilter` -ocl::boxFilter ------------------- +ocl::GaussianBlur +--------------------- Returns void -.. ocv:function:: void ocl::boxFilter(const oclMat &src, oclMat &dst, int ddepth, Size ksize, Point anchor = Point(-1, -1), int borderType = BORDER_DEFAULT) +.. ocv:function:: void ocl::GaussianBlur(const oclMat &src, oclMat &dst, Size ksize, double sigma1, double sigma2 = 0, int bordertype = BORDER_DEFAULT) :param src: The source image :param dst: The destination image; It will have the same size and the same type as src - :param ddepth: The desired depth of the destination image - - :param ksize: The smoothing kernel size. It must be positive and odd + :param ksize: The Gaussian kernel size; ksize.width and ksize.height can differ, but they both must be positive and odd. Or, they can be zero's, then they are computed from sigma - :param anchor: The anchor point. The default value Point(-1,-1) means that the anchor is at the kernel center. + :param sigma1sigma2: The Gaussian kernel standard deviations in X and Y direction. If sigmaY is zero, it is set to be equal to sigmaX. If they are both zeros, they are computed from ksize.width and ksize.height. To fully control the result regardless of possible future modification of all this semantics, it is recommended to specify all of ksize, sigmaX and sigmaY :param bordertype: Pixel extrapolation method. -Smoothes image using box filter.Supports data type: CV_8UC1, CV_8UC4, CV_32FC1 and CV_32FC4. +The function convolves the source image with the specified Gaussian kernel. In-place filtering is supported. Surpport 8UC1 8UC4 32SC1 32SC4 32FC1 32FC4 data type. ocl::Laplacian ------------------ @@ -115,16 +467,16 @@ Returns void .. ocv:function:: void ocl::convolve(const oclMat &image, const oclMat &temp1, oclMat &result) - :param image: The source image + :param image: The source image. Only ``CV_32FC1`` images are supported for now. - :param temp1: Convolution kernel, a single-channel floating point matrix. + :param temp1: Convolution kernel, a single-channel floating point matrix. The size is not greater than the ``image`` size. The type is the same as ``image``. :param result: The destination image Convolves an image with the kernel. Supports only CV_32FC1 data types and do not support ROI. ocl::bilateralFilter --------------------- +------------------------ Returns void .. ocv:function:: void ocl::bilateralFilter(const oclMat &src, oclMat &dst, int d, double sigmaColor, double sigmaSpace, int borderType=BORDER_DEFAULT) @@ -143,8 +495,42 @@ Returns void Applies bilateral filter to the image. Supports 8UC1 8UC4 data types. +ocl::adaptiveBilateralFilter +-------------------------------- +Returns void + +.. ocv:function:: void ocl::adaptiveBilateralFilter(const oclMat& src, oclMat& dst, Size ksize, double sigmaSpace, Point anchor = Point(-1, -1), int borderType=BORDER_DEFAULT) + + :param src: The source image + + :param dst: The destination image; will have the same size and the same type as src + + :param ksize: The kernel size + + :param sigmaSpace: Filter sigma in the coordinate space. Larger value of the parameter means that farther pixels will influence each other (as long as their colors are close enough; see sigmaColor). Then d>0, it specifies the neighborhood size regardless of sigmaSpace, otherwise d is proportional to sigmaSpace. + + :param borderType: Pixel extrapolation method. + +A main part of our strategy will be to load each raw pixel once, and reuse it to calculate all pixels in the output (filtered) image that need this pixel value. + +.. math:: + + \emph{O}_i = \frac{1}{W_i}\sum\limits_{j\in{N(i)}}{\frac{1}{1+\frac{(V_i-V_j)^2}{\sigma_{N{'}(i)}^2}}*\frac{1}{1+\frac{d(i,j)^2}{\sum^2}}}V_j + +Local memory organization + + +.. image:: images/adaptiveBilateralFilter.jpg + :height: 250pt + :width: 350pt + :alt: Introduction Icon + +.. note:: We partition the image to non-overlapping blocks of size (Ux, Uy). Each such block will correspond to the pixel locations where we will calculate the filter result in one workgroup. Considering neighbourhoods of sizes (kx, ky), where kx = 2 dx + 1, and ky = 2 dy + 1 (in image ML, dx = dy = 1, and kx = ky = 3), it is clear that we need to load data of size Wx = Ux + 2 dx, Wy = Uy + 2 dy. Furthermore, if (Sx, Sy) is the top left pixel coordinates for a particular block, and (Sx + Ux - 1, Sy + Uy -1) is to botom right coordinate of the block, we need to load data starting at top left coordinate (PSx, PSy) = (Sx - dx, Sy - dy), and ending at bottom right coordinate (Sx + Ux - 1 + dx, Sy + Uy - 1 + dy). The workgroup layout is (Wx,1). However, to take advantage of the natural hardware properties (preferred wavefront sizes), we restrict Wx to be a multiple of that preferred wavefront size (for current AMD hardware this is typically 64). Each thread in the workgroup will load Wy elements (under the constraint that Wx*Wy*pixel width <= max local memory). + +Applies bilateral filter to the image. Supports 8UC1 8UC3 data types. + ocl::copyMakeBorder --------------------- +----------------------- Returns void .. ocv:function:: void ocl::copyMakeBorder(const oclMat &src, oclMat &dst, int top, int bottom, int left, int right, int boardtype, const Scalar &value = Scalar()) @@ -206,7 +592,7 @@ Returns void The function erodes the source image using the specified structuring element that determines the shape of a pixel neighborhood over which the minimum is taken. Supports 8UC1 8UC4 data types. ocl::morphologyEx ------------------- +--------------------- Returns void .. ocv:function:: void ocl::morphologyEx( const oclMat &src, oclMat &dst, int op, const Mat &kernel, Point anchor = Point(-1, -1), int iterations = 1, int borderType = BORDER_CONSTANT, const Scalar &borderValue = morphologyDefaultBorderValue()) @@ -242,7 +628,6 @@ Smoothes an image and downsamples it. .. seealso:: :ocv:func:`pyrDown` - ocl::pyrUp ------------------- Upsamples an image and then smoothes it. @@ -267,7 +652,7 @@ Computes a vertical (column) sum. ocl::blendLinear -------------------- +-------------------- Performs linear blending of two images. .. ocv:function:: void ocl::blendLinear(const oclMat& img1, const oclMat& img2, const oclMat& weights1, const oclMat& weights2, oclMat& result) diff --git a/modules/ocl/doc/image_processing.rst b/modules/ocl/doc/image_processing.rst index de3669ca34..247f355484 100644 --- a/modules/ocl/doc/image_processing.rst +++ b/modules/ocl/doc/image_processing.rst @@ -3,8 +3,82 @@ Image Processing .. highlight:: cpp +ocl::meanShiftFiltering +--------------------------- +Performs mean-shift filtering for each point of the source image. + +.. ocv:function:: void ocl::meanShiftFiltering(const oclMat &src, oclMat &dst, int sp, int sr, TermCriteria criteria = TermCriteria(TermCriteria::MAX_ITER + TermCriteria::EPS, 5, 1)) + + :param src: Source image. Only ``CV_8UC4`` images are supported for now. + + :param dst: Destination image containing the color of mapped points. It has the same size and type as ``src`` . + + :param sp: Spatial window radius. + + :param sr: Color window radius. + + :param criteria: Termination criteria. See :ocv:class:`TermCriteria`. + +It maps each point of the source image into another point. As a result, you have a new color and new position of each point. + + +ocl::meanShiftProc +---------------------- +Performs a mean-shift procedure and stores information about processed points (their colors and positions) in two images. + +.. ocv:function:: void ocl::meanShiftProc(const oclMat &src, oclMat &dstr, oclMat &dstsp, int sp, int sr, TermCriteria criteria = TermCriteria(TermCriteria::MAX_ITER + TermCriteria::EPS, 5, 1)) + + :param src: Source image. Only ``CV_8UC4`` images are supported for now. + + :param dstr: Destination image containing the color of mapped points. The size and type is the same as ``src`` . + + :param dstsp: Destination image containing the position of mapped points. The size is the same as ``src`` size. The type is ``CV_16SC2`` . + + :param sp: Spatial window radius. + + :param sr: Color window radius. + + :param criteria: Termination criteria. See :ocv:class:`TermCriteria`. + +.. seealso:: :ocv:func:`ocl::meanShiftFiltering` + + +ocl::meanShiftSegmentation +------------------------------ +Performs a mean-shift segmentation of the source image and eliminates small segments. + +.. ocv:function:: void ocl::meanShiftSegmentation(const oclMat &src, Mat &dst, int sp, int sr, int minsize, TermCriteria criteria = TermCriteria(TermCriteria::MAX_ITER + TermCriteria::EPS, 5, 1)) + + :param src: Source image. Only ``CV_8UC4`` images are supported for now. + + :param dst: Segmented image with the same size and type as ``src`` . + + :param sp: Spatial window radius. + + :param sr: Color window radius. + + :param minsize: Minimum segment size. Smaller segments are merged. + + :param criteria: Termination criteria. See :ocv:class:`TermCriteria`. + +ocl::integral +----------------- +Computes an integral image. + +.. ocv:function:: void ocl::integral(const oclMat &src, oclMat &sum, oclMat &sqsum) + +.. ocv:function:: void ocl::integral(const oclMat &src, oclMat &sum) + + :param src: Source image. Only ``CV_8UC1`` images are supported for now. + + :param sum: Integral image containing 32-bit unsigned integer values packed into ``CV_32SC1`` . + + :param sqsum: Sqsum values is ``CV_32FC1`` type. + +.. seealso:: :ocv:func:`integral` + ocl::cornerHarris ------------------- +--------------------- Returns void .. ocv:function:: void ocl::cornerHarris(const oclMat &src, oclMat &dst, int blockSize, int ksize, double k, int bordertype = cv::BORDER_DEFAULT) @@ -24,7 +98,7 @@ Returns void Calculate Harris corner. ocl::cornerMinEigenVal ------------------------- +-------------------------- Returns void .. ocv:function:: void ocl::cornerMinEigenVal(const oclMat &src, oclMat &dst, int blockSize, int ksize, int bordertype = cv::BORDER_DEFAULT) @@ -53,6 +127,19 @@ Returns void Calculates histogram of one or more arrays. Supports only 8UC1 data type. +ocl::equalizeHist +--------------------- +Equalizes the histogram of a grayscale image. + +.. ocv:function:: void ocl::equalizeHist(const oclMat &mat_src, oclMat &mat_dst) + + :param mat_src: Source image. + + :param mat_dst: Destination image. + +.. seealso:: :ocv:func:`equalizeHist` + + ocl::remap ------------------ Returns void @@ -96,7 +183,7 @@ Returns void Resizes an image. Supports CV_8UC1, CV_8UC3, CV_8UC4, CV_32FC1 , CV_32FC3 and CV_32FC4 data types. ocl::warpAffine ------------------- +------------------- Returns void .. ocv:function:: void ocl::warpAffine(const oclMat &src, oclMat &dst, const Mat &M, Size dsize, int flags = INTER_LINEAR) @@ -114,7 +201,7 @@ Returns void The function warpAffine transforms the source image using the specified matrix. Supports INTER_NEAREST, INTER_LINEAR, INTER_CUBIC types. ocl::warpPerspective ---------------------- +------------------------ Returns void .. ocv:function:: void ocl::warpPerspective(const oclMat &src, oclMat &dst, const Mat &M, Size dsize, int flags = INTER_LINEAR) @@ -209,7 +296,7 @@ Builds transformation maps for perspective transformation. ocl::buildWarpAffineMaps ------------------------- +---------------------------- Builds transformation maps for affine transformation. .. ocv:function:: void ocl::buildWarpAffineMaps(const Mat& M, bool inverse, Size dsize, oclMat& xmap, oclMat& ymap) @@ -224,111 +311,4 @@ Builds transformation maps for affine transformation. :param ymap: Y values with ``CV_32FC1`` type. -.. seealso:: :ocv:func:`ocl::warpAffine` , :ocv:func:`ocl::remap` - -ocl::PyrLKOpticalFlow ---------------------- -.. ocv:class:: ocl::PyrLKOpticalFlow - -Class used for calculating an optical flow. :: - - class PyrLKOpticalFlow - { - public: - PyrLKOpticalFlow(); - - void sparse(const oclMat& prevImg, const oclMat& nextImg, const oclMat& prevPts, oclMat& nextPts, - oclMat& status, oclMat* err = 0); - - void dense(const oclMat& prevImg, const oclMat& nextImg, oclMat& u, oclMat& v, oclMat* err = 0); - - Size winSize; - int maxLevel; - int iters; - double derivLambda; - bool useInitialFlow; - float minEigThreshold; - bool getMinEigenVals; - - void releaseMemory(); - }; - -The class can calculate an optical flow for a sparse feature set or dense optical flow using the iterative Lucas-Kanade method with pyramids. - -.. seealso:: :ocv:func:`calcOpticalFlowPyrLK` - -.. note:: - - (Ocl) An example the Lucas Kanade optical flow pyramid method can be found at opencv_source_code/samples/ocl/pyrlk_optical_flow.cpp - (Ocl) An example for square detection can be found at opencv_source_code/samples/ocl/squares.cpp - -ocl::PyrLKOpticalFlow::sparse ------------------------------ -Calculate an optical flow for a sparse feature set. - -.. ocv:function:: void ocl::PyrLKOpticalFlow::sparse(const oclMat& prevImg, const oclMat& nextImg, const oclMat& prevPts, oclMat& nextPts, oclMat& status, oclMat* err = 0) - - :param prevImg: First 8-bit input image (supports both grayscale and color images). - - :param nextImg: Second input image of the same size and the same type as ``prevImg`` . - - :param prevPts: Vector of 2D points for which the flow needs to be found. It must be one row matrix with CV_32FC2 type. - - :param nextPts: Output vector of 2D points (with single-precision floating-point coordinates) containing the calculated new positions of input features in the second image. When ``useInitialFlow`` is true, the vector must have the same size as in the input. - - :param status: Output status vector (CV_8UC1 type). Each element of the vector is set to 1 if the flow for the corresponding features has been found. Otherwise, it is set to 0. - - :param err: Output vector (CV_32FC1 type) that contains the difference between patches around the original and moved points or min eigen value if ``getMinEigenVals`` is checked. It can be NULL, if not needed. - -.. seealso:: :ocv:func:`calcOpticalFlowPyrLK` - - - -ocl::PyrLKOpticalFlow::dense ------------------------------ -Calculate dense optical flow. - -.. ocv:function:: void ocl::PyrLKOpticalFlow::dense(const oclMat& prevImg, const oclMat& nextImg, oclMat& u, oclMat& v, oclMat* err = 0) - - :param prevImg: First 8-bit grayscale input image. - - :param nextImg: Second input image of the same size and the same type as ``prevImg`` . - - :param u: Horizontal component of the optical flow of the same size as input images, 32-bit floating-point, single-channel - - :param v: Vertical component of the optical flow of the same size as input images, 32-bit floating-point, single-channel - - :param err: Output vector (CV_32FC1 type) that contains the difference between patches around the original and moved points or min eigen value if ``getMinEigenVals`` is checked. It can be NULL, if not needed. - - - -ocl::PyrLKOpticalFlow::releaseMemory ------------------------------------- -Releases inner buffers memory. - -.. ocv:function:: void ocl::PyrLKOpticalFlow::releaseMemory() - - -ocl::interpolateFrames ----------------------- -Interpolate frames (images) using provided optical flow (displacement field). - -.. ocv:function:: void ocl::interpolateFrames(const oclMat& frame0, const oclMat& frame1, const oclMat& fu, const oclMat& fv, const oclMat& bu, const oclMat& bv, float pos, oclMat& newFrame, oclMat& buf) - - :param frame0: First frame (32-bit floating point images, single channel). - - :param frame1: Second frame. Must have the same type and size as ``frame0`` . - - :param fu: Forward horizontal displacement. - - :param fv: Forward vertical displacement. - - :param bu: Backward horizontal displacement. - - :param bv: Backward vertical displacement. - - :param pos: New frame position. - - :param newFrame: Output image. - - :param buf: Temporary buffer, will have width x 6*height size, CV_32FC1 type and contain 6 oclMat: occlusion masks for first frame, occlusion masks for second, interpolated forward horizontal flow, interpolated forward vertical flow, interpolated backward horizontal flow, interpolated backward vertical flow. +.. seealso:: :ocv:func:`ocl::warpAffine` , :ocv:func:`ocl::remap` \ No newline at end of file diff --git a/modules/ocl/doc/images/adaptiveBilateralFilter.jpg b/modules/ocl/doc/images/adaptiveBilateralFilter.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6508f693c38542868f5b2b08c7cadc8ec2bc915e GIT binary patch literal 65410 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 z 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 literal 0 HcmV?d00001 diff --git a/modules/ocl/doc/matrix_reductions.rst b/modules/ocl/doc/matrix_reductions.rst index aed9fa5644..350f861032 100644 --- a/modules/ocl/doc/matrix_reductions.rst +++ b/modules/ocl/doc/matrix_reductions.rst @@ -4,7 +4,7 @@ Matrix Reductions .. highlight:: cpp ocl::countNonZero ------------------- +--------------------- Returns the number of non-zero elements in src .. ocv:function:: int ocl::countNonZero(const oclMat &src) @@ -55,16 +55,26 @@ Returns the sum of matrix elements for each channel .. ocv:function:: Scalar ocl::sum(const oclMat &m) - :param m: The Source image of all depth + :param m: The Source image of all depth. Counts the sum of matrix elements for each channel. +ocl::absSum +--------------- +Returns the sum of absolute values for matrix elements. + +.. ocv:function:: Scalar ocl::absSum(const oclMat &m) + + :param m: The Source image of all depth. + +Counts the abs sum of matrix elements for each channel. + ocl::sqrSum ------------------ Returns the squared sum of matrix elements for each channel .. ocv:function:: Scalar ocl::sqrSum(const oclMat &m) - :param m: The Source image of all depth + :param m: The Source image of all depth. Counts the squared sum of matrix elements for each channel. diff --git a/modules/ocl/doc/ml_machine_learning.rst b/modules/ocl/doc/ml_machine_learning.rst new file mode 100644 index 0000000000..b09d7f2c4d --- /dev/null +++ b/modules/ocl/doc/ml_machine_learning.rst @@ -0,0 +1,88 @@ +ml.Machine Learning +============================= + +.. highlight:: cpp + +ocl::KNearestNeighbour +-------------------------- +.. ocv:class:: ocl::KNearestNeighbour + +The class implements K-Nearest Neighbors model as described in the beginning of this section. + +ocl::KNearestNeighbour +-------------------------- +Computes the weighted sum of two arrays. :: + + class CV_EXPORTS KNearestNeighbour: public CvKNearest + { + public: + KNearestNeighbour(); + ~KNearestNeighbour(); + + bool train(const Mat& trainData, Mat& labels, Mat& sampleIdx = Mat().setTo(Scalar::all(0)), + bool isRegression = false, int max_k = 32, bool updateBase = false); + + void clear(); + + void find_nearest(const oclMat& samples, int k, oclMat& lables); + + private: + /* hidden */ + }; + +ocl::KNearestNeighbour::train +--------------------------------- +Trains the model. + +.. ocv:function:: bool ocl::KNearestNeighbour::train(const Mat& trainData, Mat& labels, Mat& sampleIdx = Mat().setTo(Scalar::all(0)), bool isRegression = false, int max_k = 32, bool updateBase = false) + + :param isRegression: Type of the problem: ``true`` for regression and ``false`` for classification. + + :param maxK: Number of maximum neighbors that may be passed to the method :ocv:func:`CvKNearest::find_nearest`. + + :param updateBase: Specifies whether the model is trained from scratch (``update_base=false``), or it is updated using the new training data (``update_base=true``). In the latter case, the parameter ``maxK`` must not be larger than the original value. + +The method trains the K-Nearest model. It follows the conventions of the generic :ocv:func:`CvStatModel::train` approach with the following limitations: + +* Only ``CV_ROW_SAMPLE`` data layout is supported. +* Input variables are all ordered. +* Output variables can be either categorical ( ``is_regression=false`` ) or ordered ( ``is_regression=true`` ). +* Variable subsets (``var_idx``) and missing measurements are not supported. + +ocl::KNearestNeighbour::find_nearest +---------------------------------------- +Finds the neighbors and predicts responses for input vectors. + +.. ocv:function:: void ocl::KNearestNeighbour::find_nearest(const oclMat& samples, int k, oclMat& lables ) + + :param samples: Input samples stored by rows. It is a single-precision floating-point matrix of :math:`number\_of\_samples \times number\_of\_features` size. + + :param k: Number of used nearest neighbors. It must satisfy constraint: :math:`k \le` :ocv:func:`CvKNearest::get_max_k`. + + :param labels: Vector with results of prediction (regression or classification) for each input sample. It is a single-precision floating-point vector with ``number_of_samples`` elements. + +ocl::kmeans +--------------- +Finds centers of clusters and groups input samples around the clusters. + +.. ocv:function:: double ocl::kmeans(const oclMat &src, int K, oclMat &bestLabels, TermCriteria criteria, int attemps, int flags, oclMat ¢ers) + + :param src: Floating-point matrix of input samples, one row per sample. + + :param K: Number of clusters to split the set by. + + :param bestLabels: Input/output integer array that stores the cluster indices for every sample. + + :param criteria: The algorithm termination criteria, that is, the maximum number of iterations and/or the desired accuracy. The accuracy is specified as ``criteria.epsilon``. As soon as each of the cluster centers moves by less than ``criteria.epsilon`` on some iteration, the algorithm stops. + + :param attempts: Flag to specify the number of times the algorithm is executed using different initial labellings. The algorithm returns the labels that yield the best compactness (see the last function parameter). + + :param flags: Flag that can take the following values: + + * **KMEANS_RANDOM_CENTERS** Select random initial centers in each attempt. + + * **KMEANS_PP_CENTERS** Use ``kmeans++`` center initialization by Arthur and Vassilvitskii [Arthur2007]. + + * **KMEANS_USE_INITIAL_LABELS** During the first (and possibly the only) attempt, use the user-supplied labels instead of computing them from the initial centers. For the second and further attempts, use the random or semi-random centers. Use one of ``KMEANS_*_CENTERS`` flag to specify the exact method. + + :param centers: Output matrix of the cluster centers, one row per each cluster center. \ No newline at end of file diff --git a/modules/ocl/doc/object_detection.rst b/modules/ocl/doc/object_detection.rst index 36f3e46cf8..592d6d1a7c 100644 --- a/modules/ocl/doc/object_detection.rst +++ b/modules/ocl/doc/object_detection.rst @@ -4,7 +4,7 @@ Object Detection .. highlight:: cpp ocl::OclCascadeClassifier -------------------------- +----------------------------- .. ocv:class:: ocl::OclCascadeClassifier : public CascadeClassifier Cascade classifier class used for object detection. Supports HAAR cascade classifier in the form of cross link :: @@ -12,12 +12,11 @@ Cascade classifier class used for object detection. Supports HAAR cascade classi class CV_EXPORTS OclCascadeClassifier : public CascadeClassifier { public: - OclCascadeClassifier() {}; - ~OclCascadeClassifier() {}; - CvSeq *oclHaarDetectObjects(oclMat &gimg, CvMemStorage *storage, - double scaleFactor,int minNeighbors, - int flags, CvSize minSize = cvSize(0, 0), - CvSize maxSize = cvSize(0, 0)); + OclCascadeClassifier(){}; + ~OclCascadeClassifier(){}; + CvSeq* oclHaarDetectObjects(oclMat &gimg, CvMemStorage *storage, double scaleFactor, + int minNeighbors, int flags, CvSize minSize = cvSize(0, 0), + CvSize maxSize = cvSize(0, 0)); }; .. note:: @@ -26,24 +25,26 @@ Cascade classifier class used for object detection. Supports HAAR cascade classi ocl::OclCascadeClassifier::oclHaarDetectObjects ------------------------------------------------------ -Returns the detected objects by a list of rectangles - -.. ocv:function:: CvSeq* ocl::OclCascadeClassifier::oclHaarDetectObjects(oclMat &gimg, CvMemStorage *storage, double scaleFactor,int minNeighbors, int flags, CvSize minSize = cvSize(0, 0), CvSize maxSize = cvSize(0, 0)) +Detects objects of different sizes in the input image. - :param image: Matrix of type CV_8U containing an image where objects should be detected. +.. ocv:function:: CvSeq* ocl::OclCascadeClassifier::oclHaarDetectObjects(oclMat &gimg, CvMemStorage *storage, double scaleFactor, int minNeighbors, int flags, CvSize minSize = cvSize(0, 0), CvSize maxSize = cvSize(0, 0)) - :param imageobjectsBuff: Buffer to store detected objects (rectangles). If it is empty, it is allocated with the defaultsize. If not empty, the function searches not more than N objects, where N = sizeof(objectsBufers data)/sizeof(cv::Rect). + :param gimage: Matrix of type CV_8U containing an image where objects should be detected. :param scaleFactor: Parameter specifying how much the image size is reduced at each image scale. :param minNeighbors: Parameter specifying how many neighbors each candidate rectangle should have to retain it. + :param flags: Parameter with the same meaning for an old cascade as in the function ``cvHaarDetectObjects``. It is not used for a new cascade. + :param minSize: Minimum possible object size. Objects smaller than that are ignored. -Detects objects of different sizes in the input image,only tested for face detection now. The function returns the number of detected objects. + :param maxSize: Maximum possible object size. Objects larger than that are ignored. + +The function provides a very similar interface with that in CascadeClassifier class, except using oclMat as input image. ocl::MatchTemplateBuf ---------------------- +------------------------- .. ocv:struct:: ocl::MatchTemplateBuf Class providing memory buffers for :ocv:func:`ocl::matchTemplate` function, plus it allows to adjust some specific parameters. :: @@ -60,7 +61,7 @@ Class providing memory buffers for :ocv:func:`ocl::matchTemplate` function, plus You can use field `user_block_size` to set specific block size for :ocv:func:`ocl::matchTemplate` function. If you leave its default value `Size(0,0)` then automatic estimation of block size will be used (which is optimized for speed). By varying `user_block_size` you can reduce memory requirements at the cost of speed. ocl::matchTemplate ------------------- +---------------------- Computes a proximity map for a raster template and an image where the template is searched for. .. ocv:function:: void ocl::matchTemplate(const oclMat& image, const oclMat& templ, oclMat& result, int method) diff --git a/modules/ocl/doc/ocl.rst b/modules/ocl/doc/ocl.rst index d6d79e187a..76c1f882bf 100644 --- a/modules/ocl/doc/ocl.rst +++ b/modules/ocl/doc/ocl.rst @@ -12,7 +12,10 @@ ocl. OpenCL-accelerated Computer Vision matrix_reductions image_filtering image_processing + ml_machine_learning object_detection feature_detection_and_description + video_analysis + camera_calibration_and_3D_reconstruction .. camera_calibration_and_3d_reconstruction .. video diff --git a/modules/ocl/doc/operations_on_matrices.rst b/modules/ocl/doc/operations_on_matrices.rst index e47e720922..8cac90ebf3 100644 --- a/modules/ocl/doc/operations_on_matrices.rst +++ b/modules/ocl/doc/operations_on_matrices.rst @@ -4,7 +4,7 @@ Operations on Matrics .. highlight:: cpp ocl::oclMat::convertTo ----------------------- +-------------------------- Returns void .. ocv:function:: void ocl::oclMat::convertTo( oclMat &m, int rtype, double alpha = 1, double beta = 0 ) const @@ -20,7 +20,7 @@ Returns void The method converts source pixel values to the target datatype. saturate cast is applied in the end to avoid possible overflows. Supports CV_8UC1, CV_8UC4, CV_32SC1, CV_32SC4, CV_32FC1, CV_32FC4. ocl::oclMat::copyTo -------------------- +----------------------- Returns void .. ocv:function:: void ocl::oclMat::copyTo( oclMat &m, const oclMat &mask ) const @@ -32,7 +32,7 @@ Returns void Copies the matrix to another one. Supports CV_8UC1, CV_8UC4, CV_32SC1, CV_32SC4, CV_32FC1, CV_32FC4 ocl::oclMat::setTo ------------------- +---------------------- Returns oclMat .. ocv:function:: oclMat& ocl::oclMat::setTo(const Scalar &s, const oclMat &mask = oclMat()) @@ -84,6 +84,34 @@ Returns void Computes per-element additon between two arrays or between array and a scalar. Supports all data types except CV_8S. +ocl::addWeighted +-------------------- +Computes the weighted sum of two arrays. + +.. ocv:function:: void ocl::addWeighted(const oclMat &a, double alpha, const oclMat &b, double beta, double gama, oclMat &c) + + :param a: First source array. + + :param alpha: Weight for the first array elements. + + :param b: Second source array of the same size and channel number as ``src1`` . + + :param beta: Weight for the second array elements. + + :param c: Destination array that has the same size and number of channels as the input arrays. + + :param gamma: Scalar added to each sum. + +The function ``addWeighted`` calculates the weighted sum of two arrays as follows: + +.. math:: + + \texttt{c} (I)= \texttt{saturate} ( \texttt{a} (I)* \texttt{alpha} + \texttt{b} (I)* \texttt{beta} + \texttt{gamma} ) + +where ``I`` is a multi-dimensional index of array elements. In case of multi-channel arrays, each channel is processed independently. + +.. seealso:: :ocv:func:`addWeighted` + ocl::subtract ------------------ Returns void @@ -319,6 +347,22 @@ Returns void The function magnitude calculates magnitude of 2D vectors formed from the corresponding elements of x and y arrays. Supports only CV_32F and CV_64F data type. +ocl::magnitudeSqr +--------------------- +Computes squared magnitudes of complex matrix elements. + +.. ocv:function:: void ocl::magnitudeSqr(const oclMat &x, oclMat &magnitude) + +.. ocv:function:: void ocl::magnitudeSqr(const oclMat &x, const oclMat &y, oclMat &magnitude) + + :param x: The floating-point array of x-coordinates of the vectors + + :param y: he floating-point array of y-coordinates of the vectors; must have the same size as x + + :param magnitude: The destination array; will have the same size and same type as x + +The function magnitude calculates magnitude of 2D vectors formed from the corresponding elements of x and y arrays. Supports only CV_32F and CV_64F data type. + ocl::flip ------------------ Returns void diff --git a/modules/ocl/doc/structures_and_utility_functions.rst b/modules/ocl/doc/structures_and_utility_functions.rst index 3810d7e2d2..c3c93ecbfe 100644 --- a/modules/ocl/doc/structures_and_utility_functions.rst +++ b/modules/ocl/doc/structures_and_utility_functions.rst @@ -4,7 +4,7 @@ Data Structures and Utility Functions .. highlight:: cpp ocl::Info ---------- +------------- .. ocv:class:: ocl::Info this class should be maintained by the user and be passed to getDevice @@ -42,7 +42,7 @@ Returns void If you call this function and set a valid path, the OCL module will save the compiled kernel to the address in the first time and reload the binary since that. It can save compilation time at the runtime. ocl::getoclContext ------------------- +---------------------- Returns the pointer to the opencl context .. ocv:function:: void* ocl::getoclContext() diff --git a/modules/ocl/doc/video_analysis.rst b/modules/ocl/doc/video_analysis.rst new file mode 100644 index 0000000000..c631491f5a --- /dev/null +++ b/modules/ocl/doc/video_analysis.rst @@ -0,0 +1,570 @@ +Video Analysis +============================= + +.. highlight:: cpp + +ocl::GoodFeaturesToTrackDetector_OCL +---------------------------------------- +.. ocv:class:: ocl::GoodFeaturesToTrackDetector_OCL + +Class used for strong corners detection on an image. :: + + class GoodFeaturesToTrackDetector_OCL + { + public: + explicit GoodFeaturesToTrackDetector_OCL(int maxCorners = 1000, double qualityLevel = 0.01, double minDistance = 0.0, + int blockSize = 3, bool useHarrisDetector = false, double harrisK = 0.04); + + //! return 1 rows matrix with CV_32FC2 type + void operator ()(const oclMat& image, oclMat& corners, const oclMat& mask = oclMat()); + //! download points of type Point2f to a vector. the vector's content will be erased + void downloadPoints(const oclMat &points, std::vector &points_v); + + int maxCorners; + double qualityLevel; + double minDistance; + + int blockSize; + bool useHarrisDetector; + double harrisK; + void releaseMemory() + { + Dx_.release(); + Dy_.release(); + eig_.release(); + minMaxbuf_.release(); + tmpCorners_.release(); + } + }; + +The class finds the most prominent corners in the image. + +.. seealso:: :ocv:func:`goodFeaturesToTrack()` + +ocl::GoodFeaturesToTrackDetector_OCL::GoodFeaturesToTrackDetector_OCL +------------------------------------------------------------------------- +Constructor. + +.. ocv:function:: ocl::GoodFeaturesToTrackDetector_OCL::GoodFeaturesToTrackDetector_OCL(int maxCorners = 1000, double qualityLevel = 0.01, double minDistance = 0.0, int blockSize = 3, bool useHarrisDetector = false, double harrisK = 0.04) + + :param maxCorners: Maximum number of corners to return. If there are more corners than are found, the strongest of them is returned. + + :param qualityLevel: Parameter characterizing the minimal accepted quality of image corners. The parameter value is multiplied by the best corner quality measure, which is the minimal eigenvalue (see :ocv:func:`ocl::cornerMinEigenVal` ) or the Harris function response (see :ocv:func:`ocl::cornerHarris` ). The corners with the quality measure less than the product are rejected. For example, if the best corner has the quality measure = 1500, and the ``qualityLevel=0.01`` , then all the corners with the quality measure less than 15 are rejected. + + :param minDistance: Minimum possible Euclidean distance between the returned corners. + + :param blockSize: Size of an average block for computing a derivative covariation matrix over each pixel neighborhood. See :ocv:func:`cornerEigenValsAndVecs` . + + :param useHarrisDetector: Parameter indicating whether to use a Harris detector (see :ocv:func:`ocl::cornerHarris`) or :ocv:func:`ocl::cornerMinEigenVal`. + + :param harrisK: Free parameter of the Harris detector. + +ocl::GoodFeaturesToTrackDetector_OCL::operator () +------------------------------------------------------- +Finds the most prominent corners in the image. + +.. ocv:function:: void ocl::GoodFeaturesToTrackDetector_OCL::operator ()(const oclMat& image, oclMat& corners, const oclMat& mask = oclMat()) + + :param image: Input 8-bit, single-channel image. + + :param corners: Output vector of detected corners (it will be one row matrix with CV_32FC2 type). + + :param mask: Optional region of interest. If the image is not empty (it needs to have the type ``CV_8UC1`` and the same size as ``image`` ), it specifies the region in which the corners are detected. + +.. seealso:: :ocv:func:`goodFeaturesToTrack` + +ocl::GoodFeaturesToTrackDetector_OCL::releaseMemory +-------------------------------------------------------- +Releases inner buffers memory. + +.. ocv:function:: void ocl::GoodFeaturesToTrackDetector_OCL::releaseMemory() + +ocl::FarnebackOpticalFlow +------------------------------- +.. ocv:class:: ocl::FarnebackOpticalFlow + +Class computing a dense optical flow using the Gunnar Farneback's algorithm. :: + + class CV_EXPORTS FarnebackOpticalFlow + { + public: + FarnebackOpticalFlow(); + + int numLevels; + double pyrScale; + bool fastPyramids; + int winSize; + int numIters; + int polyN; + double polySigma; + int flags; + + void operator ()(const oclMat &frame0, const oclMat &frame1, oclMat &flowx, oclMat &flowy); + + void releaseMemory(); + + private: + /* hidden */ + }; + +ocl::FarnebackOpticalFlow::operator () +------------------------------------------ +Computes a dense optical flow using the Gunnar Farneback's algorithm. + +.. ocv:function:: void ocl::FarnebackOpticalFlow::operator ()(const oclMat &frame0, const oclMat &frame1, oclMat &flowx, oclMat &flowy) + + :param frame0: First 8-bit gray-scale input image + :param frame1: Second 8-bit gray-scale input image + :param flowx: Flow horizontal component + :param flowy: Flow vertical component + :param s: Stream + +.. seealso:: :ocv:func:`calcOpticalFlowFarneback` + +ocl::FarnebackOpticalFlow::releaseMemory +-------------------------------------------- +Releases unused auxiliary memory buffers. + +.. ocv:function:: void ocl::FarnebackOpticalFlow::releaseMemory() + + +ocl::PyrLKOpticalFlow +------------------------- +.. ocv:class:: ocl::PyrLKOpticalFlow + +Class used for calculating an optical flow. :: + + class PyrLKOpticalFlow + { + public: + PyrLKOpticalFlow(); + + void sparse(const oclMat& prevImg, const oclMat& nextImg, const oclMat& prevPts, oclMat& nextPts, + oclMat& status, oclMat* err = 0); + + void dense(const oclMat& prevImg, const oclMat& nextImg, oclMat& u, oclMat& v, oclMat* err = 0); + + Size winSize; + int maxLevel; + int iters; + double derivLambda; + bool useInitialFlow; + float minEigThreshold; + bool getMinEigenVals; + + void releaseMemory(); + + private: + /* hidden */ + }; + +The class can calculate an optical flow for a sparse feature set or dense optical flow using the iterative Lucas-Kanade method with pyramids. + +.. seealso:: :ocv:func:`calcOpticalFlowPyrLK` + +ocl::PyrLKOpticalFlow::sparse +--------------------------------- +Calculate an optical flow for a sparse feature set. + +.. ocv:function:: void ocl::PyrLKOpticalFlow::sparse(const oclMat& prevImg, const oclMat& nextImg, const oclMat& prevPts, oclMat& nextPts, oclMat& status, oclMat* err = 0) + + :param prevImg: First 8-bit input image (supports both grayscale and color images). + + :param nextImg: Second input image of the same size and the same type as ``prevImg`` . + + :param prevPts: Vector of 2D points for which the flow needs to be found. It must be one row matrix with CV_32FC2 type. + + :param nextPts: Output vector of 2D points (with single-precision floating-point coordinates) containing the calculated new positions of input features in the second image. When ``useInitialFlow`` is true, the vector must have the same size as in the input. + + :param status: Output status vector (CV_8UC1 type). Each element of the vector is set to 1 if the flow for the corresponding features has been found. Otherwise, it is set to 0. + + :param err: Output vector (CV_32FC1 type) that contains the difference between patches around the original and moved points or min eigen value if ``getMinEigenVals`` is checked. It can be NULL, if not needed. + +.. seealso:: :ocv:func:`calcOpticalFlowPyrLK` + + +ocl::PyrLKOpticalFlow::dense +--------------------------------- +Calculate dense optical flow. + +.. ocv:function:: void ocl::PyrLKOpticalFlow::dense(const oclMat& prevImg, const oclMat& nextImg, oclMat& u, oclMat& v, oclMat* err = 0) + + :param prevImg: First 8-bit grayscale input image. + + :param nextImg: Second input image of the same size and the same type as ``prevImg`` . + + :param u: Horizontal component of the optical flow of the same size as input images, 32-bit floating-point, single-channel + + :param v: Vertical component of the optical flow of the same size as input images, 32-bit floating-point, single-channel + + :param err: Output vector (CV_32FC1 type) that contains the difference between patches around the original and moved points or min eigen value if ``getMinEigenVals`` is checked. It can be NULL, if not needed. + + +ocl::PyrLKOpticalFlow::releaseMemory +---------------------------------------- +Releases inner buffers memory. + +.. ocv:function:: void ocl::PyrLKOpticalFlow::releaseMemory() + +ocl::interpolateFrames +-------------------------- +Interpolates frames (images) using provided optical flow (displacement field). + +.. ocv:function:: void ocl::interpolateFrames(const oclMat& frame0, const oclMat& frame1, const oclMat& fu, const oclMat& fv, const oclMat& bu, const oclMat& bv, float pos, oclMat& newFrame, oclMat& buf) + + :param frame0: First frame (32-bit floating point images, single channel). + + :param frame1: Second frame. Must have the same type and size as ``frame0`` . + + :param fu: Forward horizontal displacement. + + :param fv: Forward vertical displacement. + + :param bu: Backward horizontal displacement. + + :param bv: Backward vertical displacement. + + :param pos: New frame position. + + :param newFrame: Output image. + + :param buf: Temporary buffer, will have width x 6*height size, CV_32FC1 type and contain 6 oclMat: occlusion masks for first frame, occlusion masks for second, interpolated forward horizontal flow, interpolated forward vertical flow, interpolated backward horizontal flow, interpolated backward vertical flow. + + :param stream: Stream for the asynchronous version. + +ocl::KalmanFilter +-------------------- +.. ocv:class:: ocl::KalmanFilter + +Kalman filter class. :: + + class CV_EXPORTS KalmanFilter + { + public: + KalmanFilter(); + //! the full constructor taking the dimensionality of the state, of the measurement and of the control vector + KalmanFilter(int dynamParams, int measureParams, int controlParams=0, int type=CV_32F); + //! re-initializes Kalman filter. The previous content is destroyed. + void init(int dynamParams, int measureParams, int controlParams=0, int type=CV_32F); + + const oclMat& predict(const oclMat& control=oclMat()); + const oclMat& correct(const oclMat& measurement); + + oclMat statePre; //!< predicted state (x'(k)): x(k)=A*x(k-1)+B*u(k) + oclMat statePost; //!< corrected state (x(k)): x(k)=x'(k)+K(k)*(z(k)-H*x'(k)) + oclMat transitionMatrix; //!< state transition matrix (A) + oclMat controlMatrix; //!< control matrix (B) (not used if there is no control) + oclMat measurementMatrix; //!< measurement matrix (H) + oclMat processNoiseCov; //!< process noise covariance matrix (Q) + oclMat measurementNoiseCov;//!< measurement noise covariance matrix (R) + oclMat errorCovPre; //!< priori error estimate covariance matrix (P'(k)): P'(k)=A*P(k-1)*At + Q)*/ + oclMat gain; //!< Kalman gain matrix (K(k)): K(k)=P'(k)*Ht*inv(H*P'(k)*Ht+R) + oclMat errorCovPost; //!< posteriori error estimate covariance matrix (P(k)): P(k)=(I-K(k)*H)*P'(k) + private: + /* hidden */ + }; + +ocl::KalmanFilter::KalmanFilter +---------------------------------- +The constructors. + +.. ocv:function:: ocl::KalmanFilter::KalmanFilter() + +.. ocv:function:: ocl::KalmanFilter::KalmanFilter(int dynamParams, int measureParams, int controlParams=0, int type=CV_32F) + + The full constructor. + + :param dynamParams: Dimensionality of the state. + + :param measureParams: Dimensionality of the measurement. + + :param controlParams: Dimensionality of the control vector. + + :param type: Type of the created matrices that should be ``CV_32F`` or ``CV_64F``. + + +ocl::KalmanFilter::init +--------------------------- +Re-initializes Kalman filter. The previous content is destroyed. + +.. ocv:function:: void ocl::KalmanFilter::init(int dynamParams, int measureParams, int controlParams=0, int type=CV_32F) + + :param dynamParams: Dimensionalityensionality of the state. + + :param measureParams: Dimensionality of the measurement. + + :param controlParams: Dimensionality of the control vector. + + :param type: Type of the created matrices that should be ``CV_32F`` or ``CV_64F``. + + +ocl::KalmanFilter::predict +------------------------------ +Computes a predicted state. + +.. ocv:function:: const oclMat& ocl::KalmanFilter::predict(const oclMat& control=oclMat()) + + :param control: The optional input control + + +ocl::KalmanFilter::correct +----------------------------- +Updates the predicted state from the measurement. + +.. ocv:function:: const oclMat& ocl::KalmanFilter::correct(const oclMat& measurement) + + :param measurement: The measured system parameters + + +ocl::BackgroundSubtractor +---------------------------- +.. ocv:class:: ocl::BackgroundSubtractor + +Base class for background/foreground segmentation. :: + + class CV_EXPORTS BackgroundSubtractor + { + public: + //! the virtual destructor + virtual ~BackgroundSubtractor(); + //! the update operator that takes the next video frame and returns the current foreground mask as 8-bit binary image. + virtual void operator()(const oclMat& image, oclMat& fgmask, float learningRate); + + //! computes a background image + virtual void getBackgroundImage(oclMat& backgroundImage) const = 0; + }; + + +The class is only used to define the common interface for the whole family of background/foreground segmentation algorithms. + + +ocl::BackgroundSubtractor::operator() +----------------------------------------- +Computes a foreground mask. + +.. ocv:function:: void ocl::BackgroundSubtractor::operator()(const oclMat& image, oclMat& fgmask, float learningRate) + + :param image: Next video frame. + + :param fgmask: The output foreground mask as an 8-bit binary image. + + +ocl::BackgroundSubtractor::getBackgroundImage +------------------------------------------------- +Computes a background image. + +.. ocv:function:: void ocl::BackgroundSubtractor::getBackgroundImage(oclMat& backgroundImage) const + + :param backgroundImage: The output background image. + +.. note:: Sometimes the background image can be very blurry, as it contain the average background statistics. + +ocl::MOG +------------ +.. ocv:class:: ocl::MOG + +Gaussian Mixture-based Backbround/Foreground Segmentation Algorithm. :: + + class CV_EXPORTS MOG: public cv::ocl::BackgroundSubtractor + { + public: + //! the default constructor + MOG(int nmixtures = -1); + + //! re-initiaization method + void initialize(Size frameSize, int frameType); + + //! the update operator + void operator()(const oclMat& frame, oclMat& fgmask, float learningRate = 0.f); + + //! computes a background image which are the mean of all background gaussians + void getBackgroundImage(oclMat& backgroundImage) const; + + //! releases all inner buffers + void release(); + + int history; + float varThreshold; + float backgroundRatio; + float noiseSigma; + + private: + /* hidden */ + }; + +The class discriminates between foreground and background pixels by building and maintaining a model of the background. Any pixel which does not fit this model is then deemed to be foreground. The class implements algorithm described in [MOG2001]_. + +.. seealso:: :ocv:class:`BackgroundSubtractorMOG` + + +ocl::MOG::MOG +--------------------- +The constructor. + +.. ocv:function:: ocl::MOG::MOG(int nmixtures = -1) + + :param nmixtures: Number of Gaussian mixtures. + +Default constructor sets all parameters to default values. + + +ocl::MOG::operator() +------------------------ +Updates the background model and returns the foreground mask. + +.. ocv:function:: void ocl::MOG::operator()(const oclMat& frame, oclMat& fgmask, float learningRate = 0.f) + + :param frame: Next video frame. + + :param fgmask: The output foreground mask as an 8-bit binary image. + + :param stream: Stream for the asynchronous version. + + +ocl::MOG::getBackgroundImage +-------------------------------- +Computes a background image. + +.. ocv:function:: void ocl::MOG::getBackgroundImage(oclMat& backgroundImage) const + + :param backgroundImage: The output background image. + + :param stream: Stream for the asynchronous version. + + +ocl::MOG::release +--------------------- +Releases all inner buffer's memory. + +.. ocv:function:: void ocl::MOG::release() + + +ocl::MOG2 +------------- +.. ocv:class:: ocl::MOG2 + +Gaussian Mixture-based Background/Foreground Segmentation Algorithm. :: + + class CV_EXPORTS MOG2: public cv::ocl::BackgroundSubtractor + { + public: + //! the default constructor + MOG2(int nmixtures = -1); + + //! re-initiaization method + void initialize(Size frameSize, int frameType); + + //! the update operator + void operator()(const oclMat& frame, oclMat& fgmask, float learningRate = -1.0f); + + //! computes a background image which are the mean of all background gaussians + void getBackgroundImage(oclMat& backgroundImage) const; + + //! releases all inner buffers + void release(); + + int history; + + float varThreshold; + + float backgroundRatio; + + float varThresholdGen; + + float fVarInit; + float fVarMin; + float fVarMax; + + float fCT; + + bool bShadowDetection; + unsigned char nShadowDetection; + float fTau; + + private: + /* hidden */ + }; + + The class discriminates between foreground and background pixels by building and maintaining a model of the background. Any pixel which does not fit this model is then deemed to be foreground. The class implements algorithm described in [MOG2004]_. + + Here are important members of the class that control the algorithm, which you can set after constructing the class instance: + + .. ocv:member:: float backgroundRatio + + Threshold defining whether the component is significant enough to be included into the background model. ``cf=0.1 => TB=0.9`` is default. For ``alpha=0.001``, it means that the mode should exist for approximately 105 frames before it is considered foreground. + + .. ocv:member:: float varThreshold + + Threshold for the squared Mahalanobis distance that helps decide when a sample is close to the existing components (corresponds to ``Tg``). If it is not close to any component, a new component is generated. ``3 sigma => Tg=3*3=9`` is default. A smaller ``Tg`` value generates more components. A higher ``Tg`` value may result in a small number of components but they can grow too large. + + .. ocv:member:: float fVarInit + + Initial variance for the newly generated components. It affects the speed of adaptation. The parameter value is based on your estimate of the typical standard deviation from the images. OpenCV uses 15 as a reasonable value. + + .. ocv:member:: float fVarMin + + Parameter used to further control the variance. + + .. ocv:member:: float fVarMax + + Parameter used to further control the variance. + + .. ocv:member:: float fCT + + Complexity reduction parameter. This parameter defines the number of samples needed to accept to prove the component exists. ``CT=0.05`` is a default value for all the samples. By setting ``CT=0`` you get an algorithm very similar to the standard Stauffer&Grimson algorithm. + + .. ocv:member:: uchar nShadowDetection + + The value for marking shadow pixels in the output foreground mask. Default value is 127. + + .. ocv:member:: float fTau + + Shadow threshold. The shadow is detected if the pixel is a darker version of the background. ``Tau`` is a threshold defining how much darker the shadow can be. ``Tau= 0.5`` means that if a pixel is more than twice darker then it is not shadow. See [ShadowDetect2003]_. + + .. ocv:member:: bool bShadowDetection + + Parameter defining whether shadow detection should be enabled. + +.. seealso:: :ocv:class:`BackgroundSubtractorMOG2` + + +ocl::MOG2::MOG2 +----------------------- +The constructor. + +.. ocv:function:: ocl::MOG2::MOG2(int nmixtures = -1) + + :param nmixtures: Number of Gaussian mixtures. + +Default constructor sets all parameters to default values. + + +ocl::MOG2::operator() +------------------------- +Updates the background model and returns the foreground mask. + +.. ocv:function:: void ocl::MOG2::operator()( const oclMat& frame, oclMat& fgmask, float learningRate=-1.0f) + + :param frame: Next video frame. + + :param fgmask: The output foreground mask as an 8-bit binary image. + + :param stream: Stream for the asynchronous version. + + +ocl::MOG2::getBackgroundImage +--------------------------------- +Computes a background image. + +.. ocv:function:: void ocl::MOG2::getBackgroundImage(oclMat& backgroundImage) const + + :param backgroundImage: The output background image. + + :param stream: Stream for the asynchronous version. + + +ocl::MOG2::release +---------------------- +Releases all inner buffer's memory. + +.. ocv:function:: void ocl::MOG2::release() \ No newline at end of file From 074f9e33cb30199641e4365cdaaffa999fc6a744 Mon Sep 17 00:00:00 2001 From: perping Date: Thu, 26 Sep 2013 15:30:44 +0800 Subject: [PATCH 60/68] fix three warning about mog, mog2 and KNearestNeighbour. --- modules/ocl/doc/ml_machine_learning.rst | 2 +- modules/ocl/doc/video_analysis.rst | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/ocl/doc/ml_machine_learning.rst b/modules/ocl/doc/ml_machine_learning.rst index b09d7f2c4d..321cec9dba 100644 --- a/modules/ocl/doc/ml_machine_learning.rst +++ b/modules/ocl/doc/ml_machine_learning.rst @@ -5,7 +5,7 @@ ml.Machine Learning ocl::KNearestNeighbour -------------------------- -.. ocv:class:: ocl::KNearestNeighbour +.. ocv:class:: ocl::KNearestNeighbour : public ocl::CvKNearest The class implements K-Nearest Neighbors model as described in the beginning of this section. diff --git a/modules/ocl/doc/video_analysis.rst b/modules/ocl/doc/video_analysis.rst index c631491f5a..599c0f9b63 100644 --- a/modules/ocl/doc/video_analysis.rst +++ b/modules/ocl/doc/video_analysis.rst @@ -361,7 +361,7 @@ Computes a background image. ocl::MOG ------------ -.. ocv:class:: ocl::MOG +.. ocv:class:: ocl::MOG : public ocl::BackgroundSubtractor Gaussian Mixture-based Backbround/Foreground Segmentation Algorithm. :: @@ -441,7 +441,7 @@ Releases all inner buffer's memory. ocl::MOG2 ------------- -.. ocv:class:: ocl::MOG2 +.. ocv:class:: ocl::MOG2 : public ocl::BackgroundSubtractor Gaussian Mixture-based Background/Foreground Segmentation Algorithm. :: From 0a695eb314dff32ae44d9645ae1f491b9516a699 Mon Sep 17 00:00:00 2001 From: Andrey Pavlenko Date: Thu, 26 Sep 2013 17:06:06 +0400 Subject: [PATCH 61/68] hortening the file names (HighDynamicRange -> HDR) --- doc/tutorials/contrib/retina_model/retina_model.rst | 2 +- modules/contrib/doc/retina/index.rst | 8 ++++---- ...pping.cpp => OpenEXRimages_HDR_Retina_toneMapping.cpp} | 4 ++-- ...cpp => OpenEXRimages_HDR_Retina_toneMapping_video.cpp} | 8 ++++---- 4 files changed, 11 insertions(+), 11 deletions(-) rename samples/cpp/{OpenEXRimages_HighDynamicRange_Retina_toneMapping.cpp => OpenEXRimages_HDR_Retina_toneMapping.cpp} (98%) rename samples/cpp/{OpenEXRimages_HighDynamicRange_Retina_toneMapping_video.cpp => OpenEXRimages_HDR_Retina_toneMapping_video.cpp} (97%) diff --git a/doc/tutorials/contrib/retina_model/retina_model.rst b/doc/tutorials/contrib/retina_model/retina_model.rst index 184bb343e1..313e3bc129 100644 --- a/doc/tutorials/contrib/retina_model/retina_model.rst +++ b/doc/tutorials/contrib/retina_model/retina_model.rst @@ -44,7 +44,7 @@ In the following image, as your retina does, local luminance adaptation, spatial :align: center -*Note :* image sample can be downloaded from the `OpenEXR website `_. Regarding this demonstration, before retina processing, input image has been linearly rescaled within 0-255 keeping its channels float format. 5% of its histogram ends has been cut (mostly removes wrong HDR pixels). Check out the sample *opencv/samples/cpp/OpenEXRimages_HighDynamicRange_Retina_toneMapping.cpp* for similar processing. The following demonstration will only consider classical 8bit/channel images. +*Note :* image sample can be downloaded from the `OpenEXR website `_. Regarding this demonstration, before retina processing, input image has been linearly rescaled within 0-255 keeping its channels float format. 5% of its histogram ends has been cut (mostly removes wrong HDR pixels). Check out the sample *opencv/samples/cpp/OpenEXRimages_HDR_Retina_toneMapping.cpp* for similar processing. The following demonstration will only consider classical 8bit/channel images. The retina model output channels ================================ diff --git a/modules/contrib/doc/retina/index.rst b/modules/contrib/doc/retina/index.rst index 3d1f59c8e9..24af092de5 100644 --- a/modules/contrib/doc/retina/index.rst +++ b/modules/contrib/doc/retina/index.rst @@ -65,8 +65,8 @@ The retina can be settled up with various parameters, by default, the retina can .. Sample code:: - * An example on retina tone mapping can be found at opencv_source_code/samples/cpp/OpenEXRimages_HighDynamicRange_Retina_toneMapping.cpp - * An example on retina tone mapping on video input can be found at opencv_source_code/samples/cpp/OpenEXRimages_HighDynamicRange_Retina_toneMapping.cpp + * An example on retina tone mapping can be found at opencv_source_code/samples/cpp/OpenEXRimages_HDR_Retina_toneMapping.cpp + * An example on retina tone mapping on video input can be found at opencv_source_code/samples/cpp/OpenEXRimages_HDR_Retina_toneMapping.cpp * A complete example illustrating the retina interface can be found at opencv_source_code/samples/cpp/retinaDemo.cpp Description @@ -121,13 +121,13 @@ Take a look at the provided C++ examples provided with OpenCV : **Note :** This demo generates the file *RetinaDefaultParameters.xml* which contains the default parameters of the retina. Then, rename this as *RetinaSpecificParameters.xml*, adjust the parameters the way you want and reload the program to check the effect. -* **samples/cpp/OpenEXRimages_HighDynamicRange_Retina_toneMapping.cpp** shows how to use the retina to perform High Dynamic Range (HDR) luminance compression +* **samples/cpp/OpenEXRimages_HDR_Retina_toneMapping.cpp** shows how to use the retina to perform High Dynamic Range (HDR) luminance compression Then, take a HDR image using bracketing with your camera and generate an OpenEXR image and then process it using the demo. Typical use, supposing that you have the OpenEXR image *memorial.exr* (present in the samples/cpp/ folder) - **OpenCVReleaseFolder/bin/OpenEXRimages_HighDynamicRange_Retina_toneMapping memorial.exr** + **OpenCVReleaseFolder/bin/OpenEXRimages_HDR_Retina_toneMapping memorial.exr** Note that some sliders are made available to allow you to play with luminance compression. diff --git a/samples/cpp/OpenEXRimages_HighDynamicRange_Retina_toneMapping.cpp b/samples/cpp/OpenEXRimages_HDR_Retina_toneMapping.cpp similarity index 98% rename from samples/cpp/OpenEXRimages_HighDynamicRange_Retina_toneMapping.cpp rename to samples/cpp/OpenEXRimages_HDR_Retina_toneMapping.cpp index 1636e41f66..e89003d19a 100644 --- a/samples/cpp/OpenEXRimages_HighDynamicRange_Retina_toneMapping.cpp +++ b/samples/cpp/OpenEXRimages_HDR_Retina_toneMapping.cpp @@ -15,10 +15,10 @@ static void help(std::string errorMessage) { std::cout<<"Program init error : "< WARNING : image index number of digits cannot exceed 10"< to process images from memorial020d.exr to memorial045d.exr"< Date: Fri, 27 Sep 2013 09:50:37 +0800 Subject: [PATCH 62/68] fix warning about addWeighted and remove magnitudeSqr(). --- modules/ocl/doc/operations_on_matrices.rst | 24 ++++------------------ 1 file changed, 4 insertions(+), 20 deletions(-) diff --git a/modules/ocl/doc/operations_on_matrices.rst b/modules/ocl/doc/operations_on_matrices.rst index 8cac90ebf3..8276a0b00d 100644 --- a/modules/ocl/doc/operations_on_matrices.rst +++ b/modules/ocl/doc/operations_on_matrices.rst @@ -88,17 +88,17 @@ ocl::addWeighted -------------------- Computes the weighted sum of two arrays. -.. ocv:function:: void ocl::addWeighted(const oclMat &a, double alpha, const oclMat &b, double beta, double gama, oclMat &c) +.. ocv:function:: void ocl::addWeighted(const oclMat& src1, double alpha, const oclMat& src2, double beta, double gama, oclMat& dst) - :param a: First source array. + :param src1: First source array. :param alpha: Weight for the first array elements. - :param b: Second source array of the same size and channel number as ``src1`` . + :param src2: Second source array of the same size and channel number as ``src1`` . :param beta: Weight for the second array elements. - :param c: Destination array that has the same size and number of channels as the input arrays. + :param dst: Destination array that has the same size and number of channels as the input arrays. :param gamma: Scalar added to each sum. @@ -347,22 +347,6 @@ Returns void The function magnitude calculates magnitude of 2D vectors formed from the corresponding elements of x and y arrays. Supports only CV_32F and CV_64F data type. -ocl::magnitudeSqr ---------------------- -Computes squared magnitudes of complex matrix elements. - -.. ocv:function:: void ocl::magnitudeSqr(const oclMat &x, oclMat &magnitude) - -.. ocv:function:: void ocl::magnitudeSqr(const oclMat &x, const oclMat &y, oclMat &magnitude) - - :param x: The floating-point array of x-coordinates of the vectors - - :param y: he floating-point array of y-coordinates of the vectors; must have the same size as x - - :param magnitude: The destination array; will have the same size and same type as x - -The function magnitude calculates magnitude of 2D vectors formed from the corresponding elements of x and y arrays. Supports only CV_32F and CV_64F data type. - ocl::flip ------------------ Returns void From 8cb0921252d694d1aa48fa786a7d63d381b1ace4 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Fri, 27 Sep 2013 12:17:55 +0400 Subject: [PATCH 63/68] pr #1464 reverted as breaking the build for Linux without libv4l library. --- modules/highgui/src/cap_v4l.cpp | 25 ++++++------------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/modules/highgui/src/cap_v4l.cpp b/modules/highgui/src/cap_v4l.cpp index cfb115328a..045c6f889c 100644 --- a/modules/highgui/src/cap_v4l.cpp +++ b/modules/highgui/src/cap_v4l.cpp @@ -248,10 +248,6 @@ make & enjoy! #define CHANNEL_NUMBER 1 #define MAX_CAMERAS 8 -// Extra global values declared for error capture -#define CAPV4L2_CAMERA_UNPLUGGED 2 -#define CAPV4L2_OK 1 -#define CAPV4L2_FAIL 0 // default and maximum number of V4L buffers, not including last, 'special' buffer #define MAX_V4L_BUFFERS 10 @@ -1240,10 +1236,10 @@ static int read_frame_v4l2(CvCaptureCAM_V4L* capture) { //set timestamp in capture struct to be timestamp of most recent frame capture->timestamp = buf.timestamp; - return CAPV4L2_CAMERA_UNPLUGGED; + return 1; } -static int mainloop_v4l2(CvCaptureCAM_V4L* capture) { +static void mainloop_v4l2(CvCaptureCAM_V4L* capture) { unsigned int count; count = 1; @@ -1277,11 +1273,8 @@ static int mainloop_v4l2(CvCaptureCAM_V4L* capture) { break; } - switch(int readresult = read_frame_v4l2(capture) ) - case CAM_UNPLUGGED: - return CAPV4L2_FAIL; - default: - return CAPV4L2_OK; + if (read_frame_v4l2 (capture)) + break; } } } @@ -1361,10 +1354,7 @@ static int icvGrabFrameCAM_V4L(CvCaptureCAM_V4L* capture) { { // skip first frame. it is often bad -- this is unnotied in traditional apps, // but could be fatal if bad jpeg is enabled - if(!mainloop_v4l2(capture)){ - fprintf( stderr, "HIGHGUI ERROR: V4L: Could not capture image.\n"); - return 0; - } + mainloop_v4l2(capture); } #endif @@ -1377,10 +1367,7 @@ static int icvGrabFrameCAM_V4L(CvCaptureCAM_V4L* capture) { if (V4L2_SUPPORT == 1) { - if(!mainloop_v4l2(capture)){ - fprintf( stderr, "HIGHGUI ERROR: V4L: Could not capture image.\n"); - return 0; - } + mainloop_v4l2(capture); } #endif /* HAVE_CAMV4L2 */ From d1f3a628320ccad907ac32e1a9122eca87f7ffc2 Mon Sep 17 00:00:00 2001 From: Vladislav Vinogradov Date: Fri, 27 Sep 2013 14:44:58 +0400 Subject: [PATCH 64/68] fix for bug #3291: add INSTALL command for TBB target --- 3rdparty/tbb/CMakeLists.txt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/3rdparty/tbb/CMakeLists.txt b/3rdparty/tbb/CMakeLists.txt index c728440f27..3cec47cfb7 100644 --- a/3rdparty/tbb/CMakeLists.txt +++ b/3rdparty/tbb/CMakeLists.txt @@ -247,9 +247,11 @@ if(ENABLE_SOLUTION_FOLDERS) set_target_properties(tbb PROPERTIES FOLDER "3rdparty") endif() -if(NOT BUILD_SHARED_LIBS) - install(TARGETS tbb ARCHIVE DESTINATION ${OPENCV_3P_LIB_INSTALL_PATH} COMPONENT main) -endif() +install(TARGETS tbb + RUNTIME DESTINATION bin COMPONENT main + LIBRARY DESTINATION ${OPENCV_LIB_INSTALL_PATH} COMPONENT main + ARCHIVE DESTINATION ${OPENCV_3P_LIB_INSTALL_PATH} COMPONENT main + ) # get TBB version ocv_parse_header("${tbb_src_dir}/include/tbb/tbb_stddef.h" TBB_VERSION_LINES TBB_VERSION_MAJOR TBB_VERSION_MINOR TBB_INTERFACE_VERSION CACHE) From b9e542e35e1abb4825e40ba8dc2407a0fd073ebb Mon Sep 17 00:00:00 2001 From: Nghia Ho Date: Sat, 28 Sep 2013 21:46:48 +1000 Subject: [PATCH 65/68] Missing closedir with opendir --- samples/cpp/latentsvm_multidetect.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/samples/cpp/latentsvm_multidetect.cpp b/samples/cpp/latentsvm_multidetect.cpp index 619c54b849..be8f2f8fed 100644 --- a/samples/cpp/latentsvm_multidetect.cpp +++ b/samples/cpp/latentsvm_multidetect.cpp @@ -96,6 +96,8 @@ static void readDirectory( const string& directoryName, vector& filename else filenames.push_back( string(dent->d_name) ); } + + closedir( dir ); } #endif From f69ccfa43da8cdb40fbbb9e4edf3a845604eb86d Mon Sep 17 00:00:00 2001 From: peng xiao Date: Mon, 16 Sep 2013 11:23:53 +0800 Subject: [PATCH 66/68] Add opencl svm. --- modules/ocl/include/opencv2/ocl/ocl.hpp | 20 + modules/ocl/src/opencl/svm.cl | 209 ++++ modules/ocl/src/svm.cpp | 1197 +++++++++++++++++++++++ 3 files changed, 1426 insertions(+) create mode 100644 modules/ocl/src/opencl/svm.cl create mode 100644 modules/ocl/src/svm.cpp diff --git a/modules/ocl/include/opencv2/ocl/ocl.hpp b/modules/ocl/include/opencv2/ocl/ocl.hpp index d3dbded34d..ab12dc2cbf 100644 --- a/modules/ocl/include/opencv2/ocl/ocl.hpp +++ b/modules/ocl/include/opencv2/ocl/ocl.hpp @@ -1900,6 +1900,26 @@ namespace cv private: oclMat samples_ocl; }; + /*!*************** SVM *************!*/ + class CV_EXPORTS CvSVM_OCL : public CvSVM + { + public: + CvSVM_OCL(); + + CvSVM_OCL(const cv::Mat& trainData, const cv::Mat& responses, + const cv::Mat& varIdx=cv::Mat(), const cv::Mat& sampleIdx=cv::Mat(), + CvSVMParams params=CvSVMParams()); + CV_WRAP float predict( const int row_index, Mat& src, bool returnDFVal=false ) const; + CV_WRAP void predict( cv::InputArray samples, cv::OutputArray results ) const; + CV_WRAP float predict( const cv::Mat& sample, bool returnDFVal=false ) const; + float predict( const CvMat* samples, CV_OUT CvMat* results ) const; + + protected: + float predict( const int row_index, int row_len, Mat& src, bool returnDFVal=false ) const; + void create_kernel(); + void create_solver(); + }; + /*!*************** END *************!*/ } } #if defined _MSC_VER && _MSC_VER >= 1200 diff --git a/modules/ocl/src/opencl/svm.cl b/modules/ocl/src/opencl/svm.cl new file mode 100644 index 0000000000..074ceb0598 --- /dev/null +++ b/modules/ocl/src/opencl/svm.cl @@ -0,0 +1,209 @@ +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2010-2013, Institute Of Software Chinese Academy Of Science, all rights reserved. +// Copyright (C) 2010-2013, Advanced Micro Devices, Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// @Authors +// Erping Pang, erping@multicorewareinc.com +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other oclMaterials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors as is and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +// +#if defined (DOUBLE_SUPPORT) +#ifdef cl_khr_fp64 +#pragma OPENCL EXTENSION cl_khr_fp64:enable +#elif defined (cl_amd_fp64) +#pragma OPENCL EXTENSION cl_amd_fp64:enable +#endif +#define TYPE double +#else +#define TYPE float +#endif +#if defined ADDEXP +#define EXP(X) exp(X) +#else +#define EXP(X) X +#endif +#if defined ADDPOW +#define POW(X,Y) pow(fabs(X),(Y)) +#else +#define POW(X,Y) X +#endif +#define FLT_MAX 3.402823466e+38F +#define MAX_VAL (FLT_MAX*1e-3) + +__kernel void svm_linear(__global float* src, int src_step, __global float* src2, int src2_step, __global TYPE* dst, int dst_step, int src_rows, int src2_cols, + int width, TYPE alpha, TYPE beta) +{ + const int col = get_global_id(0); + const int row = get_global_id(1); + + if(row < src_rows && col < src2_cols) + { + int t = 0; + TYPE temp = 0.0; + for(t = 0; t < width - 16; t += 16) + { + float16 t0 = vload16(0, src + row * src_step + t); + float16 t1 = vload16(0, src2 + col * src2_step + t); + t0 *= t1; + temp += t0.s0 + t0.s1 + t0.s2 + t0.s3 + t0.s4 + t0.s5 + t0.s6 + t0.s7 + + t0.s8 + t0.s9 + t0.sa + t0.sb + t0.sc + t0.sd + t0.se + t0.sf; + } + for(; t < width; t++) + { + temp += src[row * src_step + t] * src2[col * src2_step + t]; + } + + TYPE temp1 = (TYPE) (temp * alpha + beta); + + if( temp1 > MAX_VAL ) + { + dst[row * dst_step + col] = MAX_VAL; + } + else + { + dst[row * dst_step + col] = temp1; + } + + } + +} +__kernel void svm_sigmod(__global float* src, int src_step, __global float* src2, int src2_step, __global TYPE* dst, int dst_step, int src_rows, int src2_cols, + int width, TYPE alpha, TYPE beta) +{ + const int col = get_global_id(0); + const int row = get_global_id(1); + + if(row < src_rows && col < src2_cols) + { + int t = 0; + TYPE temp = 0.0; + for(t = 0; t < width - 16; t += 16) + { + float16 t0 = vload16(0, src + row * src_step + t); + float16 t1 = vload16(0, src2 + col * src2_step + t); + t0 *= t1; + temp += t0.s0 + t0.s1 + t0.s2 + t0.s3 + t0.s4 + t0.s5 + t0.s6 + t0.s7 + + t0.s8 + t0.s9 + t0.sa + t0.sb + t0.sc + t0.sd + t0.se + t0.sf; + } + for(; t < width; t++) + { + temp += src[row * src_step + t] * src2[col * src2_step + t]; + } + TYPE tp = (TYPE) (temp * alpha + beta); + TYPE e = exp(-fabs(tp)); + TYPE temp1; + if(tp > 0) + { + temp1 = (TYPE)((1. - e) / (1. + e)); + } + else + { + temp1 = (TYPE)((e - 1.) / (e + 1.)); + } + + if( temp1 > MAX_VAL ) + { + dst[row * dst_step + col] = MAX_VAL; + } + else + { + dst[row * dst_step + col] = temp1; + } + } + +} +__kernel void svm_poly(__global float* src, int src_step, __global float* src2, int src2_step, __global TYPE* dst, int dst_step, int src_rows, int src2_cols, + int width, TYPE alpha, TYPE beta, TYPE degree) +{ + const int col = get_global_id(0); + const int row = get_global_id(1); + + if(row < src_rows && col < src2_cols) + { + int t = 0; + TYPE temp = 0.0; + for(t = 0; t < width - 16; t += 16) + { + float16 t0 = vload16(0, src + row * src_step + t); + float16 t1 = vload16(0, src2 + col * src2_step + t); + t0 *= t1; + temp += t0.s0 + t0.s1 + t0.s2 + t0.s3 + t0.s4 + t0.s5 + t0.s6 + t0.s7 + + t0.s8 + t0.s9 + t0.sa + t0.sb + t0.sc + t0.sd + t0.se + t0.sf; + } + for(; t < width; t++) + { + temp += src[row * src_step + t] * src2[col * src2_step + t]; + } + TYPE temp1 = (TYPE)(POW((temp * alpha + beta), degree)); + + if( temp1 > MAX_VAL ) + { + dst[row * dst_step + col] = MAX_VAL; + } + else + { + dst[row * dst_step + col] = temp1; + } + } + +} +__kernel void svm_rbf(__global float* src, int src_step, __global float* src2, int src2_step, __global TYPE* dst, int dst_step, int src_rows, int src2_cols, + int width, TYPE gamma) +{ + const int col = get_global_id(0); + const int row = get_global_id(1); + + if(row < src_rows && col < src2_cols) + { + int t = 0; + TYPE temp = 0.0; + for(t = 0; t < width - 16; t += 16) + { + float16 t0 = vload16(0, src + row * src_step + t); + float16 t1 = vload16(0, src2 + col * src2_step + t); + t0 = (t0 - t1) * (t0 - t1); + temp += t0.s0 + t0.s1 + t0.s2 + t0.s3 + t0.s4 + t0.s5 + t0.s6 + t0.s7 + + t0.s8 + t0.s9 + t0.sa + t0.sb + t0.sc + t0.sd + t0.se + t0.sf; + } + for(; t < width; t++) + { + temp += (src[row * src_step + t] - src2[col * src2_step + t]) * (src[row * src_step + t] - src2[col * src2_step + t]); + } + TYPE temp1 = EXP((TYPE)(temp * gamma)); + + if( temp1 > MAX_VAL ) + { + dst[row * dst_step + col] = MAX_VAL; + } + else + { + dst[row * dst_step + col] = temp1; + } + } +} \ No newline at end of file diff --git a/modules/ocl/src/svm.cpp b/modules/ocl/src/svm.cpp new file mode 100644 index 0000000000..212c0d56af --- /dev/null +++ b/modules/ocl/src/svm.cpp @@ -0,0 +1,1197 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2010-2013, Institute Of Software Chinese Academy Of Science, all rights reserved. +// Copyright (C) 2010-2013, Advanced Micro Devices, Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// @Authors +// Erping Pang, erping@multicorewareinc.com +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other oclMaterials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ +#include "precomp.hpp" +using namespace cv; +using namespace ocl; + +#if 1 +typedef float Qfloat; +#define QFLOAT_TYPE CV_32F +#else +typedef double Qfloat; +#define QFLOAT_TYPE CV_64F +#endif + +namespace cv +{ +namespace ocl +{ +///////////////////////////OpenCL kernel strings/////////////////////////// +extern const char *svm; +} +} +class CvSVMKernel_ocl: public CvSVMKernel +{ +public: + typedef void (CvSVMKernel_ocl::*Calc_ocl)( int vec_count, const int row_idx, Qfloat* results, Mat& src); + CvSVMKernel_ocl(const CvSVMParams* params, Calc_ocl _calc_func , Calc _calc_func1); + + Calc_ocl calc_func_ocl; + bool create( const CvSVMParams* params, Calc_ocl _calc_func, Calc _calc_func1); + + void calc( int vcount, const int row_idx, Qfloat* results, Mat& src); + void calc_linear( int vec_count, const int row_idx, Qfloat* results, Mat& src); + + void calc_poly( int vec_count, const int row_idx, Qfloat* results, Mat& src); + void calc_sigmoid( int vec_count, const int row_idx, Qfloat* results, Mat& src); + void calc_non_rbf_base( int vec_count, const int row_idx, Qfloat* results, Mat& src); + void calc_rbf( int vec_count, const int row_idx, Qfloat* results, Mat& src); +}; +class CvSVMSolver_ocl: public CvSVMSolver +{ +public: + CvSVMSolver_ocl(); + CvSVMSolver_ocl(const CvSVMParams *); + float* get_row_base( int i, bool* _existed, Mat& src); + bool solve_generic( CvSVMSolutionInfo& si ); + float* get_row( int i, float* dst, Mat& src); +}; + +typedef struct CvSparseVecElem32f +{ + int idx; + float val; +} CvSparseVecElem32f; +static int icvCmpSparseVecElems( const void* a, const void* b ) +{ + return ((CvSparseVecElem32f*)a)->idx - ((CvSparseVecElem32f*)b)->idx; +} +void cvPreparePredictData( const CvArr* sample, int dims_all, const CvMat* comp_idx, + int class_count, const CvMat* prob, float** row_sample, + int as_sparse CV_DEFAULT(0) ); +void cvPreparePredictData( const CvArr* _sample, int dims_all, + const CvMat* comp_idx, int class_count, + const CvMat* prob, float** _row_sample, + int as_sparse ) +{ + float* row_sample = 0; + int* inverse_comp_idx = 0; + + CV_FUNCNAME( "cvPreparePredictData" ); + + __BEGIN__; + + const CvMat* sample = (const CvMat*)_sample; + float* sample_data; + int sample_step; + int is_sparse = CV_IS_SPARSE_MAT(sample); + int d, sizes[CV_MAX_DIM]; + int i, dims_selected; + int vec_size; + + if( !is_sparse && !CV_IS_MAT(sample) ) + { + CV_ERROR( !sample ? CV_StsNullPtr : CV_StsBadArg, "The sample is not a valid vector" ); + } + + if( cvGetElemType( sample ) != CV_32FC1 ) + { + CV_ERROR( CV_StsUnsupportedFormat, "Input sample must have 32fC1 type" ); + } + + CV_CALL( d = cvGetDims( sample, sizes )); + + if( !((is_sparse && d == 1) || (!is_sparse && d == 2 && (sample->rows == 1 || sample->cols == 1))) ) + { + CV_ERROR( CV_StsBadSize, "Input sample must be 1-dimensional vector" ); + } + + if( d == 1 ) + { + sizes[1] = 1; + } + + if( sizes[0] + sizes[1] - 1 != dims_all ) + CV_ERROR( CV_StsUnmatchedSizes, + "The sample size is different from what has been used for training" ); + + if( !_row_sample ) + { + CV_ERROR( CV_StsNullPtr, "INTERNAL ERROR: The row_sample pointer is NULL" ); + } + + if( comp_idx && (!CV_IS_MAT(comp_idx) || comp_idx->rows != 1 || + CV_MAT_TYPE(comp_idx->type) != CV_32SC1) ) + { + CV_ERROR( CV_StsBadArg, "INTERNAL ERROR: invalid comp_idx" ); + } + + dims_selected = comp_idx ? comp_idx->cols : dims_all; + + if( prob ) + { + if( !CV_IS_MAT(prob) ) + { + CV_ERROR( CV_StsBadArg, "The output matrix of probabilities is invalid" ); + } + + if( (prob->rows != 1 && prob->cols != 1) || + (CV_MAT_TYPE(prob->type) != CV_32FC1 && + CV_MAT_TYPE(prob->type) != CV_64FC1) ) + CV_ERROR( CV_StsBadSize, + "The matrix of probabilities must be 1-dimensional vector of 32fC1 type" ); + + if( prob->rows + prob->cols - 1 != class_count ) + CV_ERROR( CV_StsUnmatchedSizes, + "The vector of probabilities must contain as many elements as " + "the number of classes in the training set" ); + } + + vec_size = !as_sparse ? dims_selected * sizeof(row_sample[0]) : + (dims_selected + 1) * sizeof(CvSparseVecElem32f); + + if( CV_IS_MAT(sample) ) + { + sample_data = sample->data.fl; + sample_step = CV_IS_MAT_CONT(sample->type) ? 1 : sample->step / sizeof(row_sample[0]); + + if( !comp_idx && CV_IS_MAT_CONT(sample->type) && !as_sparse ) + { + *_row_sample = sample_data; + } + else + { + CV_CALL( row_sample = (float*)cvAlloc( vec_size )); + + if( !comp_idx ) + for( i = 0; i < dims_selected; i++ ) + { + row_sample[i] = sample_data[sample_step * i]; + } + else + { + int* comp = comp_idx->data.i; + for( i = 0; i < dims_selected; i++ ) + { + row_sample[i] = sample_data[sample_step * comp[i]]; + } + } + + *_row_sample = row_sample; + } + + if( as_sparse ) + { + const float* src = (const float*)row_sample; + CvSparseVecElem32f* dst = (CvSparseVecElem32f*)row_sample; + + dst[dims_selected].idx = -1; + for( i = dims_selected - 1; i >= 0; i-- ) + { + dst[i].idx = i; + dst[i].val = src[i]; + } + } + } + else + { + CvSparseNode* node; + CvSparseMatIterator mat_iterator; + const CvSparseMat* sparse = (const CvSparseMat*)sample; + assert( is_sparse ); + + node = cvInitSparseMatIterator( sparse, &mat_iterator ); + CV_CALL( row_sample = (float*)cvAlloc( vec_size )); + + if( comp_idx ) + { + CV_CALL( inverse_comp_idx = (int*)cvAlloc( dims_all * sizeof(int) )); + memset( inverse_comp_idx, -1, dims_all * sizeof(int) ); + for( i = 0; i < dims_selected; i++ ) + { + inverse_comp_idx[comp_idx->data.i[i]] = i; + } + } + + if( !as_sparse ) + { + memset( row_sample, 0, vec_size ); + + for( ; node != 0; node = cvGetNextSparseNode(&mat_iterator) ) + { + int idx = *CV_NODE_IDX( sparse, node ); + if( inverse_comp_idx ) + { + idx = inverse_comp_idx[idx]; + if( idx < 0 ) + { + continue; + } + } + row_sample[idx] = *(float*)CV_NODE_VAL( sparse, node ); + } + } + else + { + CvSparseVecElem32f* ptr = (CvSparseVecElem32f*)row_sample; + + for( ; node != 0; node = cvGetNextSparseNode(&mat_iterator) ) + { + int idx = *CV_NODE_IDX( sparse, node ); + if( inverse_comp_idx ) + { + idx = inverse_comp_idx[idx]; + if( idx < 0 ) + { + continue; + } + } + ptr->idx = idx; + ptr->val = *(float*)CV_NODE_VAL( sparse, node ); + ptr++; + } + + qsort( row_sample, ptr - (CvSparseVecElem32f*)row_sample, + sizeof(ptr[0]), icvCmpSparseVecElems ); + ptr->idx = -1; + } + + *_row_sample = row_sample; + } + + __END__; + + if( inverse_comp_idx ) + { + cvFree( &inverse_comp_idx ); + } + + if( cvGetErrStatus() < 0 && _row_sample ) + { + cvFree( &row_sample ); + *_row_sample = 0; + } +} +float CvSVM_OCL::predict( const int row_index, int row_len, Mat& src, bool returnDFVal ) const +{ + assert( kernel ); + + (void)row_len; + + int class_count = class_labels ? class_labels->cols : + params.svm_type == ONE_CLASS ? 1 : 0; + + float result = 0; + cv::AutoBuffer _buffer(sv_total + (class_count + 1) * 2); + float* buffer = _buffer; + + if( params.svm_type == EPS_SVR || + params.svm_type == NU_SVR || + params.svm_type == ONE_CLASS ) + { + CvSVMDecisionFunc* df = (CvSVMDecisionFunc*)decision_func; + int i, sv_count = df->sv_count; + double sum = -df->rho; + + ((CvSVMKernel_ocl*)kernel)->calc( sv_count, row_index, buffer, src); + for( i = 0; i < sv_count; i++ ) + { + sum += buffer[i] * df->alpha[i]; + } + + result = params.svm_type == ONE_CLASS ? (float)(sum > 0) : (float)sum; + } + else if( params.svm_type == C_SVC || + params.svm_type == NU_SVC ) + { + CvSVMDecisionFunc* df = (CvSVMDecisionFunc*)decision_func; + int* vote = (int*)(buffer + sv_total); + int i, j, k; + + memset( vote, 0, class_count * sizeof(vote[0])); + ((CvSVMKernel_ocl*)kernel)->calc( sv_total, row_index, buffer, src); + double sum = 0.; + + for( i = 0; i < class_count; i++ ) + { + for( j = i + 1; j < class_count; j++, df++ ) + { + sum = -df->rho; + int sv_count = df->sv_count; + for( k = 0; k < sv_count; k++ ) + { + sum += df->alpha[k] * buffer[df->sv_index[k]]; + } + + vote[sum > 0 ? i : j]++; + } + } + + for( i = 1, k = 0; i < class_count; i++ ) + { + if( vote[i] > vote[k] ) + { + k = i; + } + } + result = returnDFVal && class_count == 2 ? (float)sum : (float)(class_labels->data.i[k]); + } + else + CV_Error( CV_StsBadArg, "INTERNAL ERROR: Unknown SVM type, " + "the SVM structure is probably corrupted" ); + + return result; +} +float CvSVM_OCL::predict( const Mat& _sample, bool returnDFVal ) const +{ + CvMat sample = _sample; + return CvSVM::predict(&sample, returnDFVal); +} +float CvSVM_OCL::predict( const int row_index, Mat& src, bool returnDFVal) const +{ + float result = 0; + + result = predict( row_index, get_var_count(), src, returnDFVal); + + return result; +} +#undef get_C +#define get_C(i) (C[y[i]>0]) +#undef is_upper_bound +#define is_upper_bound(i) (alpha_status[i] > 0) +#undef is_lower_bound +#define is_lower_bound(i) (alpha_status[i] < 0) +#undef update_alpha_status +#define update_alpha_status(i) \ + alpha_status[i] = (schar)(alpha[i] >= get_C(i) ? 1 : alpha[i] <= 0 ? -1 : 0) + +CvSVMSolver_ocl::CvSVMSolver_ocl(const CvSVMParams* _params) +{ + params = _params; +} +float* CvSVMSolver_ocl::get_row( int i, float* dst, Mat& src ) +{ + bool existed = false; + float* row = get_row_base( i, &existed, src); + return (this->*get_row_func)( i, row, dst, existed ); +} +float* CvSVMSolver_ocl::get_row_base( int i, bool* _existed, Mat& src ) +{ + int i1 = i < sample_count ? i : i - sample_count; + CvSVMKernelRow* row = rows + i1; + bool existed = row->data != 0; + Qfloat* data; + + if( existed || cache_size <= 0 ) + { + CvSVMKernelRow* del_row = existed ? row : lru_list.prev; + data = del_row->data; + assert( data != 0 ); + + // delete row from the LRU list + del_row->data = 0; + del_row->prev->next = del_row->next; + del_row->next->prev = del_row->prev; + } + else + { + data = (Qfloat*)cvMemStorageAlloc( storage, cache_line_size ); + cache_size -= cache_line_size; + } + + // insert row into the LRU list + row->data = data; + row->prev = &lru_list; + row->next = lru_list.next; + row->prev->next = row->next->prev = row; + + if( !existed ) + { + ((CvSVMKernel_ocl*)kernel)->calc( sample_count, i1, row->data, src); + } + + if( _existed ) + { + *_existed = existed; + } + + return row->data; +} + +void matmul_sigmod(oclMat & src, oclMat & src2, oclMat & dst, int src_rows, int src2_cols, int var_count, double alpha1, double beta1) +{ + Context *clCxt = Context::getContext(); + string kernelName = "svm_sigmod"; + int src_step = (int)src.step / src.elemSize(); + int src2_step = (int)src2.step / src2.elemSize(); + int dst_step = (int)dst.step / dst.elemSize(); + int x = MIN(16, src_rows); + int y = MIN(16, src2_cols); + size_t localThreads[] = {x, y, 1}; + size_t globalThreads[] = {src2_cols, src_rows, 1}; + int width = var_count; + + vector< pair > args; + args.push_back(make_pair(sizeof(cl_mem), (void* )&src.data)); + args.push_back(make_pair(sizeof(cl_int), (void* )&src_step)); + args.push_back(make_pair(sizeof(cl_mem), (void* )&src2.data)); + args.push_back(make_pair(sizeof(cl_int), (void* )&src2_step)); + args.push_back(make_pair(sizeof(cl_mem), (void* )&dst.data)); + args.push_back(make_pair(sizeof(cl_int), (void* )&dst_step)); + args.push_back(make_pair(sizeof(cl_int), (void* )&src_rows)); + args.push_back(make_pair(sizeof(cl_int), (void* )&src2_cols)); + args.push_back(make_pair(sizeof(cl_int), (void* )&width)); + + float alpha = 0.0f, beta = 0.0f; + if(!Context::getContext()->supportsFeature(Context::CL_DOUBLE)) + { + alpha = (float)alpha1; + beta = (float)beta1; + args.push_back(make_pair(sizeof(cl_float), (void* )&alpha)); + args.push_back(make_pair(sizeof(cl_float), (void* )&beta)); + } + else + { + args.push_back(make_pair(sizeof(cl_double), (void* )&alpha1)); + args.push_back(make_pair(sizeof(cl_double), (void* )&beta1)); + } + openCLExecuteKernel(clCxt, &svm, kernelName, globalThreads, localThreads, args, -1, -1); +} +void matmul_poly(oclMat & src, oclMat & src2, oclMat & dst, int src_rows, int src2_cols, int var_count, double alpha1, double beta1, double degree1, bool flag) +{ + Context *clCxt = Context::getContext(); + string kernelName = "svm_poly"; + int src_step = (int)src.step / src.elemSize(); + int src2_step = (int)src2.step / src2.elemSize(); + int dst_step = (int)dst.step / dst.elemSize(); + int x = MIN(16, src_rows); + int y = MIN(16, src2_cols); + size_t localThreads[] = {x, y, 1}; + size_t globalThreads[] = {src2_cols, src_rows, 1}; + int width = var_count; + + char build_options[50]; + + if(flag) + { + sprintf(build_options, "-D ADDPOW"); + } + vector< pair > args; + args.push_back(make_pair(sizeof(cl_mem), (void* )&src.data)); + args.push_back(make_pair(sizeof(cl_int), (void* )&src_step)); + args.push_back(make_pair(sizeof(cl_mem), (void* )&src2.data)); + args.push_back(make_pair(sizeof(cl_int), (void* )&src2_step)); + args.push_back(make_pair(sizeof(cl_mem), (void* )&dst.data)); + args.push_back(make_pair(sizeof(cl_int), (void* )&dst_step)); + args.push_back(make_pair(sizeof(cl_int), (void* )&src_rows)); + args.push_back(make_pair(sizeof(cl_int), (void* )&src2_cols)); + args.push_back(make_pair(sizeof(cl_int), (void* )&width)); + + float alpha = 0.0f, beta = 0.0f, degree = 0.0f; + if(!Context::getContext()->supportsFeature(Context::CL_DOUBLE)) + { + alpha = (float)alpha1; + beta = (float)beta1; + degree = (float)degree1; + args.push_back(make_pair(sizeof(cl_float), (void* )&alpha)); + args.push_back(make_pair(sizeof(cl_float), (void* )&beta)); + args.push_back(make_pair(sizeof(cl_float), (void* )°ree)); + } + else + { + args.push_back(make_pair(sizeof(cl_double), (void* )&alpha1)); + args.push_back(make_pair(sizeof(cl_double), (void* )&beta1)); + args.push_back(make_pair(sizeof(cl_double), (void* )°ree1)); + } + openCLExecuteKernel(clCxt, &svm, kernelName, globalThreads, localThreads, args, -1, -1, build_options); +} +void matmul_linear(oclMat & src, oclMat & src2, oclMat & dst, int src_rows, int src2_cols, int var_count, double alpha1, double beta1) +{ + Context *clCxt = Context::getContext(); + string kernelName = "svm_linear"; + int src_step = (int)src.step / src.elemSize(); + int src2_step = (int)src2.step / src2.elemSize(); + int dst_step = (int)dst.step / dst.elemSize(); + int x = MIN(16, src_rows); + int y = MIN(16, src2_cols); + size_t localThreads[] = {x, y, 1}; + size_t globalThreads[] = {src2_cols, src_rows, 1}; + int width = var_count; + + vector< pair > args; + args.push_back(make_pair(sizeof(cl_mem), (void* )&src.data)); + args.push_back(make_pair(sizeof(cl_int), (void* )&src_step)); + args.push_back(make_pair(sizeof(cl_mem), (void* )&src2.data)); + args.push_back(make_pair(sizeof(cl_int), (void* )&src2_step)); + args.push_back(make_pair(sizeof(cl_mem), (void* )&dst.data)); + args.push_back(make_pair(sizeof(cl_int), (void* )&dst_step)); + args.push_back(make_pair(sizeof(cl_int), (void* )&src_rows)); + args.push_back(make_pair(sizeof(cl_int), (void* )&src2_cols)); + args.push_back(make_pair(sizeof(cl_int), (void* )&width)); + + float alpha = 0.0f, beta = 0.0f; + if(!Context::getContext()->supportsFeature(Context::CL_DOUBLE)) + { + alpha = (float)alpha1; + beta = (float)beta1; + args.push_back(make_pair(sizeof(cl_float), (void* )&alpha)); + args.push_back(make_pair(sizeof(cl_float), (void* )&beta)); + } + else + { + args.push_back(make_pair(sizeof(cl_double), (void* )&alpha1)); + args.push_back(make_pair(sizeof(cl_double), (void* )&beta1)); + } + openCLExecuteKernel(clCxt, &svm, kernelName, globalThreads, localThreads, args, -1, -1); +} +void matmul_rbf(oclMat& src, oclMat& src_e, oclMat& dst, int src_rows, int src2_cols, int var_count, double gamma1, bool flag) +{ + + Context *clCxt = Context::getContext(); + + string kernelName = "svm_rbf"; + + int width = var_count; + int src_step = (int)src.step / src.elemSize(); + int src_e_step = (int)src_e.step / src_e.elemSize(); + int dst_step = (int)dst.step / dst.elemSize(); + + int x = MIN(16, src_rows); + int y = MIN(16, src2_cols); + size_t localThreads[] = {x, y, 1}; + size_t globalThreads[] = {src2_cols, src_rows, 1}; + char build_options[50]; + + if(flag) + { + sprintf(build_options, "-D ADDEXP"); + } + vector< pair > args; + args.push_back(make_pair(sizeof(cl_mem), (void* )&src.data)); + args.push_back(make_pair(sizeof(cl_int), (void* )&src_step)); + args.push_back(make_pair(sizeof(cl_mem), (void* )&src_e.data)); + args.push_back(make_pair(sizeof(cl_int), (void* )&src_e_step)); + args.push_back(make_pair(sizeof(cl_mem), (void* )&dst.data)); + args.push_back(make_pair(sizeof(cl_int), (void* )&dst_step)); + args.push_back(make_pair(sizeof(cl_int), (void* )&src_rows)); + args.push_back(make_pair(sizeof(cl_int), (void* )&src2_cols)); + args.push_back(make_pair(sizeof(cl_int), (void* )&width)); + float gamma = 0.0f; + if(!Context::getContext()->supportsFeature(Context::CL_DOUBLE)) + { + gamma = (float)gamma1; + args.push_back(make_pair(sizeof(cl_float), (void* )&gamma)); + } + else + { + args.push_back(make_pair(sizeof(cl_double), (void* )&gamma1)); + } + + openCLExecuteKernel(clCxt, &svm, kernelName, globalThreads, localThreads, args, -1, -1, build_options); +} +float CvSVM_OCL::predict(const CvMat* samples, CV_OUT CvMat* results) const +{ + int var_count = get_var_count(); + int sample_count = samples->rows; + + //float* row_sample = 0; + Mat src_temp = Mat(sample_count, var_count, CV_32FC1); + CV_FUNCNAME( "CvSVM::predict" ); + + + for(int i = 0; i < samples->rows; i++) + { + __BEGIN__; + CvMat sample; + float* row_sample = 0; + cvGetRow( samples, &sample, i ); + int class_count; + if( !kernel ) + { + CV_ERROR( CV_StsBadArg, "The SVM should be trained first" ); + } + + class_count = class_labels ? class_labels->cols : + params.svm_type == ONE_CLASS ? 1 : 0; + + CV_CALL( cvPreparePredictData(&sample, var_all, var_idx, + class_count, 0, &row_sample )); + for(int j = 0; j < var_count; ++j) + { + src_temp.at(i, j) = row_sample[j]; + } + __END__; + } + + Mat dst1; + double alpha1 = 0.0, beta1 = 0.0, gamma1 = 0.0, degree1 = 0.0; + if(params.kernel_type == CvSVM::LINEAR) + { + alpha1 = 1; + beta1 = 0; + } + if(params.kernel_type == CvSVM::POLY) + { + alpha1 = params.gamma; + beta1 = params.coef0; + degree1 = params.degree; + } + if(params.kernel_type == CvSVM::SIGMOID) + { + alpha1 = - 2 * params.gamma; + beta1 = - 2 * params.coef0; + } + if(params.kernel_type == CvSVM::RBF) + { + gamma1 = - params.gamma; + } + + Mat sv_temp = Mat(sv_total, var_count, CV_32FC1, Scalar::all(0)); + + + for(int i = 0; i < sv_total; ++i) + { + for(int j = 0; j < var_count; ++j) + { + sv_temp.at(i, j) = sv[i][j]; + } + } + oclMat src(sample_count, var_count, CV_32FC1, Scalar::all(0)); + oclMat sv_; + + src.upload(src_temp); + oclMat dst; + +#if defined HAVE_CLAMDBLAS + + dst = oclMat(sample_count, sv_total, CV_32FC1); + oclMat src3(sample_count, sv_total, CV_32FC1, Scalar::all(1)); + if(params.kernel_type != CvSVM::RBF) + { + Mat sv_temp1; + transpose(sv_temp, sv_temp1); + sv_.upload(sv_temp1); + gemm(src, sv_, alpha1, src3, beta1, dst); + } + +#else + + if(!Context::getContext()->supportsFeature(Context::CL_DOUBLE)) + { + dst = oclMat(sample_count, sv_total, CV_32FC1); + } + else + { + dst = oclMat(sample_count, sv_total, CV_64FC1); + } + if(params.kernel_type == CvSVM::LINEAR) + { + sv_.upload(sv_temp); + matmul_linear(src, sv_, dst, sample_count, sv_total, var_count, alpha1, beta1); + } + if( params.kernel_type == CvSVM::SIGMOID) + { + sv_.upload(sv_temp); + matmul_sigmod(src, sv_, dst, sample_count, sv_total, var_count, alpha1, beta1); + } + + if(params.kernel_type == CvSVM::POLY) + { + sv_.upload(sv_temp); + if(sample_count > 0) + { + matmul_poly(src, sv_, dst, sample_count, sv_total, var_count, alpha1, beta1, degree1, true); + } + else + { + matmul_poly(src, sv_, dst, sample_count, sv_total, var_count, alpha1, beta1, degree1, false); + } + } +#endif + + if(params.kernel_type == CvSVM::RBF) + { + sv_.upload(sv_temp); + if(!Context::getContext()->supportsFeature(Context::CL_DOUBLE)) + { + dst = oclMat(sample_count, sv_total, CV_32FC1); + } + else + { + dst = oclMat(sample_count, sv_total, CV_64FC1); + } + if(sample_count > 0) + { + matmul_rbf(src, sv_, dst, sample_count, sv_total, var_count, gamma1, true); + } + else + { + matmul_rbf(src, sv_, dst, sample_count, sv_total, var_count, gamma1, false); + } + } + dst.download(dst1); + + float result = 0; + for(int i = 0; i < samples->rows; i++ ) + { + int r = (int)this->predict(i, dst1); + if (results) + { + results->data.fl[i] = (float)r; + } + if (i == 0) + { + result = (float)r; + } + } + return result; +} +void CvSVM_OCL::predict( cv::InputArray _samples, cv::OutputArray _results ) const +{ + _results.create(_samples.size().height, 1, CV_32F); + CvMat samples = _samples.getMat(), results = _results.getMat(); + predict(&samples, &results); +} +bool CvSVMSolver_ocl::solve_generic( CvSVMSolutionInfo& si ) +{ + int iter = 0; + int i, j, k; + + // 1. initialize gradient and alpha status + for( i = 0; i < alpha_count; i++ ) + { + update_alpha_status(i); + G[i] = b[i]; + if( fabs(G[i]) > 1e200 ) + { + return false; + } + } + Mat dst1; + double alpha1 = 0.0, beta1 = 0.0, gamma1 = 0.0, degree1 = 0.0; + if(params->kernel_type == CvSVM::LINEAR) + { + alpha1 = 1; + beta1 = 0; + } + if(params->kernel_type == CvSVM::POLY) + { + alpha1 = params->gamma; + beta1 = params->coef0; + degree1 = params->degree; + } + if(params->kernel_type == CvSVM::SIGMOID) + { + alpha1 = -2 * params->gamma; + beta1 = -2 * params->coef0; + } + if(params->kernel_type == CvSVM::RBF) + { + gamma1 = -params->gamma; + } + Mat src1 = Mat(sample_count, var_count, CV_32FC1); + + for(int i = 0; i < sample_count; ++i) + { + for(int j = 0; j < var_count; ++j) + { + src1.at(i, j) = samples[i][j]; + } + } + oclMat src, src_e; + src.upload(src1); + oclMat dst; + +#if defined HAVE_CLAMDBLAS + + dst = oclMat(sample_count, sample_count, CV_32FC1); + oclMat src3(sample_count, sample_count, CV_32FC1, Scalar::all(1)); + if(params->kernel_type != CvSVM::RBF) + { + ocl::transpose(src, src_e); + gemm(src, src_e, alpha1, src3, beta1, dst); + } + +#else + if(!Context::getContext()->supportsFeature(Context::CL_DOUBLE)) + { + dst = oclMat(sample_count, sample_count, CV_32FC1); + } + else + { + dst = oclMat(sample_count, sample_count, CV_64FC1); + } + if(params->kernel_type == CvSVM::LINEAR ) + { + src_e = src; + matmul_linear(src, src_e, dst, sample_count, sample_count, var_count, alpha1, beta1); + } + if( params->kernel_type == CvSVM::SIGMOID) + { + src_e = src; + matmul_sigmod(src, src_e, dst, sample_count, sample_count, var_count, alpha1, beta1); + } + + if(params->kernel_type == CvSVM::POLY) + { + src_e = src; + if(sample_count > 0) + { + matmul_poly(src, src_e, dst, sample_count, sample_count, var_count, alpha1, beta1, degree1, true); + } + else + { + matmul_poly(src, src_e, dst, sample_count, sample_count, var_count, alpha1, beta1, degree1, false); + } + } + +#endif + + if(params->kernel_type == CvSVM::RBF) + { + src_e = src; + if(!Context::getContext()->supportsFeature(Context::CL_DOUBLE)) + { + dst = oclMat(sample_count, sample_count, CV_32FC1); + } + else + { + dst = oclMat(sample_count, sample_count, CV_64FC1); + } + if(sample_count > 0) + { + matmul_rbf(src, src_e, dst, sample_count, sample_count, var_count, gamma1, true); + } + else + { + matmul_rbf(src, src_e, dst, sample_count, sample_count, var_count, gamma1, false); + } + } + dst.download(dst1); + for( i = 0; i < alpha_count; i++ ) + { + if( !is_lower_bound(i) ) + { + const Qfloat *Q_i = CvSVMSolver::get_row( i, buf[0]); + double alpha_i = alpha[i]; + + for( j = 0; j < alpha_count; j++ ) + { + G[j] += alpha_i * Q_i[j]; + } + } + } + + // 2. optimization loop + for(;;) + { + const Qfloat *Q_i, *Q_j; + double C_i, C_j; + double old_alpha_i, old_alpha_j, alpha_i, alpha_j; + double delta_alpha_i, delta_alpha_j; + +#ifdef _DEBUG + for( i = 0; i < alpha_count; i++ ) + { + if( fabs(G[i]) > 1e+300 ) + { + return false; + } + + if( fabs(alpha[i]) > 1e16 ) + { + return false; + } + } +#endif + + if( (this->*select_working_set_func)( i, j ) != 0 || iter++ >= max_iter ) + { + break; + } + Q_i = get_row( i, buf[0], dst1); + Q_j = get_row( j, buf[1], dst1); + + C_i = get_C(i); + C_j = get_C(j); + + alpha_i = old_alpha_i = alpha[i]; + alpha_j = old_alpha_j = alpha[j]; + + if( y[i] != y[j] ) + { + double denom = Q_i[i] + Q_j[j] + 2 * Q_i[j]; + double delta = (-G[i] - G[j]) / MAX(fabs(denom), FLT_EPSILON); + double diff = alpha_i - alpha_j; + alpha_i += delta; + alpha_j += delta; + + if( diff > 0 && alpha_j < 0 ) + { + alpha_j = 0; + alpha_i = diff; + } + else if( diff <= 0 && alpha_i < 0 ) + { + alpha_i = 0; + alpha_j = -diff; + } + + if( diff > C_i - C_j && alpha_i > C_i ) + { + alpha_i = C_i; + alpha_j = C_i - diff; + } + else if( diff <= C_i - C_j && alpha_j > C_j ) + { + alpha_j = C_j; + alpha_i = C_j + diff; + } + } + else + { + double denom = Q_i[i] + Q_j[j] - 2 * Q_i[j]; + double delta = (G[i] - G[j]) / MAX(fabs(denom), FLT_EPSILON); + double sum = alpha_i + alpha_j; + alpha_i -= delta; + alpha_j += delta; + + if( sum > C_i && alpha_i > C_i ) + { + alpha_i = C_i; + alpha_j = sum - C_i; + } + else if( sum <= C_i && alpha_j < 0) + { + alpha_j = 0; + alpha_i = sum; + } + + if( sum > C_j && alpha_j > C_j ) + { + alpha_j = C_j; + alpha_i = sum - C_j; + } + else if( sum <= C_j && alpha_i < 0 ) + { + alpha_i = 0; + alpha_j = sum; + } + } + // update alpha + alpha[i] = alpha_i; + alpha[j] = alpha_j; + update_alpha_status(i); + update_alpha_status(j); + + // update G + delta_alpha_i = alpha_i - old_alpha_i; + delta_alpha_j = alpha_j - old_alpha_j; + + for( k = 0; k < alpha_count; k++ ) + { + G[k] += Q_i[k] * delta_alpha_i + Q_j[k] * delta_alpha_j; + } + } + + // calculate rho + (this->*calc_rho_func)( si.rho, si.r ); + + // calculate objective value + for( i = 0, si.obj = 0; i < alpha_count; i++ ) + { + si.obj += alpha[i] * (G[i] + b[i]); + } + + si.obj *= 0.5; + + si.upper_bound_p = C[1]; + si.upper_bound_n = C[0]; + + return true; +} + +void CvSVMKernel_ocl::calc( int vcount, const int row_idx, Qfloat* results, Mat& src) +{ + //const Qfloat max_val = (Qfloat)(FLT_MAX*1e-3); + //int j; + (this->*calc_func_ocl)( vcount, row_idx, results, src); + +#if defined HAVE_CLAMDBLAS + const Qfloat max_val = (Qfloat)(FLT_MAX * 1e-3); + int j; + for( j = 0; j < vcount; j++ ) + { + if( results[j] > max_val ) + { + results[j] = max_val; + } + } +#endif +} +bool CvSVMKernel_ocl::create( const CvSVMParams* _params, Calc_ocl _calc_func, Calc _calc_func1 ) +{ + clear(); + params = _params; + calc_func_ocl = _calc_func; + calc_func = _calc_func1; + if( !calc_func_ocl ) + calc_func_ocl = params->kernel_type == CvSVM::RBF ? &CvSVMKernel_ocl::calc_rbf : + params->kernel_type == CvSVM::POLY ? &CvSVMKernel_ocl::calc_poly : + params->kernel_type == CvSVM::SIGMOID ? &CvSVMKernel_ocl::calc_sigmoid : + &CvSVMKernel_ocl::calc_linear; + if( !calc_func) + calc_func = params->kernel_type == CvSVM::RBF ? &CvSVMKernel::calc_rbf : + params->kernel_type == CvSVM::POLY ? &CvSVMKernel::calc_poly : + params->kernel_type == CvSVM::SIGMOID ? &CvSVMKernel::calc_sigmoid : + &CvSVMKernel::calc_linear; + return true; +} +CvSVMKernel_ocl::CvSVMKernel_ocl(const CvSVMParams* params, CvSVMKernel_ocl::Calc_ocl _calc_func, CvSVMKernel::Calc _calc_func1) +{ + CvSVMKernel::clear(); + CvSVMKernel_ocl::create( params, _calc_func, _calc_func1 ); +} +void CvSVMKernel_ocl::calc_non_rbf_base( int vcount, const int row_idx, Qfloat* results, Mat& src) +{ +#if defined HAVE_CLAMDBLAS + + for(int i = 0; i < vcount; i++) + { + results[i] = (Qfloat) * src.ptr(row_idx, i); + } +#else + if(!Context::getContext()->supportsFeature(Context::CL_DOUBLE)) + { + for(int i = 0; i < vcount; i++) + { + results[i] = (Qfloat) * src.ptr(row_idx, i); + } + } + else + { + for(int i = 0; i < vcount; i++) + { + results[i] = (Qfloat) * src.ptr(row_idx, i); + } + } +#endif +} +void CvSVMKernel_ocl::calc_rbf( int vcount, const int row_idx, Qfloat* results, Mat& src) +{ + if(!Context::getContext()->supportsFeature(Context::CL_DOUBLE)) + { + for(int m = 0; m < vcount; m++) + { + results[m] = (Qfloat) * src.ptr(row_idx, m); + } + } + else + { + for(int m = 0; m < vcount; m++) + { + results[m] = (Qfloat) * src.ptr(row_idx, m); + } + } +} +void CvSVMKernel_ocl::calc_linear( int vcount, const int row_idx, Qfloat* results, Mat& src ) +{ + calc_non_rbf_base( vcount, row_idx, results, src); +} + +void CvSVMKernel_ocl::calc_poly( int vcount, const int row_idx, Qfloat* results, Mat& src) +{ + + calc_non_rbf_base( vcount, row_idx, results, src); + +#if defined HAVE_CLAMDBLAS + + CvMat R = cvMat( 1, vcount, QFLOAT_TYPE, results ); + if( vcount > 0 ) + { + cvPow( &R, &R, params->degree ); + } +#endif +} + + +void CvSVMKernel_ocl::calc_sigmoid( int vcount, const int row_idx, Qfloat* results, Mat& src) +{ + calc_non_rbf_base( vcount, row_idx, results, src); + // TODO: speedup this +#if defined HAVE_CLAMDBLAS + for(int j = 0; j < vcount; j++ ) + { + Qfloat t = results[j]; + double e = exp(-fabs(t)); + if( t > 0 ) + { + results[j] = (Qfloat)((1. - e) / (1. + e)); + } + else + { + results[j] = (Qfloat)((e - 1.) / (e + 1.)); + } + } +#endif +} +CvSVM_OCL::CvSVM_OCL() +{ + CvSVM::CvSVM(); +} + +CvSVM_OCL::CvSVM_OCL( const Mat& _train_data, const Mat& _responses, + const Mat& _var_idx, const Mat& _sample_idx, CvSVMParams _params ) +{ + decision_func = 0; + class_labels = 0; + class_weights = 0; + storage = 0; + var_idx = 0; + kernel = 0; + solver = 0; + default_model_name = "my_svm"; + + train( _train_data, _responses, _var_idx, _sample_idx, _params ); +} + +void CvSVM_OCL::create_kernel() +{ + kernel = new CvSVMKernel_ocl(¶ms, 0, 0); +} +void CvSVM_OCL::create_solver( ) +{ + solver = new CvSVMSolver_ocl(¶ms); +} From 422396ef6a8b77ee7ffe03394425dc830e66ccac Mon Sep 17 00:00:00 2001 From: peng xiao Date: Mon, 16 Sep 2013 13:52:56 +0800 Subject: [PATCH 67/68] fix build error --- modules/ocl/src/svm.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/modules/ocl/src/svm.cpp b/modules/ocl/src/svm.cpp index 212c0d56af..c3df581f40 100644 --- a/modules/ocl/src/svm.cpp +++ b/modules/ocl/src/svm.cpp @@ -450,7 +450,8 @@ float* CvSVMSolver_ocl::get_row_base( int i, bool* _existed, Mat& src ) return row->data; } -void matmul_sigmod(oclMat & src, oclMat & src2, oclMat & dst, int src_rows, int src2_cols, int var_count, double alpha1, double beta1) +#ifndef HAVE_CLAMDBLAS +static void matmul_sigmod(oclMat & src, oclMat & src2, oclMat & dst, int src_rows, int src2_cols, int var_count, double alpha1, double beta1) { Context *clCxt = Context::getContext(); string kernelName = "svm_sigmod"; @@ -489,7 +490,7 @@ void matmul_sigmod(oclMat & src, oclMat & src2, oclMat & dst, int src_rows, int } openCLExecuteKernel(clCxt, &svm, kernelName, globalThreads, localThreads, args, -1, -1); } -void matmul_poly(oclMat & src, oclMat & src2, oclMat & dst, int src_rows, int src2_cols, int var_count, double alpha1, double beta1, double degree1, bool flag) +static void matmul_poly(oclMat & src, oclMat & src2, oclMat & dst, int src_rows, int src2_cols, int var_count, double alpha1, double beta1, double degree1, bool flag) { Context *clCxt = Context::getContext(); string kernelName = "svm_poly"; @@ -537,7 +538,7 @@ void matmul_poly(oclMat & src, oclMat & src2, oclMat & dst, int src_rows, int sr } openCLExecuteKernel(clCxt, &svm, kernelName, globalThreads, localThreads, args, -1, -1, build_options); } -void matmul_linear(oclMat & src, oclMat & src2, oclMat & dst, int src_rows, int src2_cols, int var_count, double alpha1, double beta1) +static void matmul_linear(oclMat & src, oclMat & src2, oclMat & dst, int src_rows, int src2_cols, int var_count, double alpha1, double beta1) { Context *clCxt = Context::getContext(); string kernelName = "svm_linear"; @@ -576,7 +577,9 @@ void matmul_linear(oclMat & src, oclMat & src2, oclMat & dst, int src_rows, int } openCLExecuteKernel(clCxt, &svm, kernelName, globalThreads, localThreads, args, -1, -1); } -void matmul_rbf(oclMat& src, oclMat& src_e, oclMat& dst, int src_rows, int src2_cols, int var_count, double gamma1, bool flag) +#endif // #ifndef HAVE_CLAMDBLAS + +static void matmul_rbf(oclMat& src, oclMat& src_e, oclMat& dst, int src_rows, int src2_cols, int var_count, double gamma1, bool flag) { Context *clCxt = Context::getContext(); @@ -621,6 +624,7 @@ void matmul_rbf(oclMat& src, oclMat& src_e, oclMat& dst, int src_rows, int src2_ openCLExecuteKernel(clCxt, &svm, kernelName, globalThreads, localThreads, args, -1, -1, build_options); } + float CvSVM_OCL::predict(const CvMat* samples, CV_OUT CvMat* results) const { int var_count = get_var_count(); @@ -1169,7 +1173,7 @@ void CvSVMKernel_ocl::calc_sigmoid( int vcount, const int row_idx, Qfloat* resul } CvSVM_OCL::CvSVM_OCL() { - CvSVM::CvSVM(); + CvSVM(); } CvSVM_OCL::CvSVM_OCL( const Mat& _train_data, const Mat& _responses, From 315c054379610c044cdc6b67d62935f678994de4 Mon Sep 17 00:00:00 2001 From: peng xiao Date: Tue, 17 Sep 2013 08:50:13 +0800 Subject: [PATCH 68/68] Fix new blank line at EOF. --- modules/ocl/test/test_ml.cpp | 178 ++++++++++++++++++++++++++++++++++- 1 file changed, 177 insertions(+), 1 deletion(-) diff --git a/modules/ocl/test/test_ml.cpp b/modules/ocl/test/test_ml.cpp index 834fc4e377..af86d35a65 100644 --- a/modules/ocl/test/test_ml.cpp +++ b/modules/ocl/test/test_ml.cpp @@ -121,4 +121,180 @@ TEST_P(KNN, Accuracy) } INSTANTIATE_TEST_CASE_P(OCL_ML, KNN, Combine(Values(6, 5), Values(Size(200, 400), Size(300, 600)), Values(4, 3), Values(false, true))); -#endif // HAVE_OPENCL \ No newline at end of file + +////////////////////////////////SVM///////////////////////////////////////////////// +PARAM_TEST_CASE(SVM_OCL, int, int, int) +{ + cv::Size size; + int kernel_type; + int svm_type; + Mat src, labels, samples, labels_predict; + int K; + cv::RNG rng ; + + virtual void SetUp() + { + + kernel_type = GET_PARAM(0); + svm_type = GET_PARAM(1); + K = GET_PARAM(2); + rng = TS::ptr()->get_rng(); + cv::Size size = cv::Size(MWIDTH, MHEIGHT); + src.create(size, CV_32FC1); + labels.create(1, size.height, CV_32SC1); + int row_idx = 0; + const int max_number = size.height / K - 1; + CV_Assert(K <= size.height); + for(int i = 0; i < K; i++ ) + { + Mat center_row_header = src.row(row_idx); + center_row_header.setTo(0); + int nchannel = center_row_header.channels(); + for(int j = 0; j < nchannel; j++) + { + center_row_header.at(0, i * nchannel + j) = 500.0; + } + labels.at(0, row_idx) = i; + for(int j = 0; (j < max_number) || + (i == K - 1 && j < max_number + size.height % K); j ++) + { + Mat cur_row_header = src.row(row_idx + 1 + j); + center_row_header.copyTo(cur_row_header); + Mat tmpmat = randomMat(rng, cur_row_header.size(), cur_row_header.type(), 1, 100, false); + cur_row_header += tmpmat; + labels.at(0, row_idx + 1 + j) = i; + } + row_idx += 1 + max_number; + } + labels.convertTo(labels, CV_32FC1); + cv::Size test_size = cv::Size(MWIDTH, 100); + samples.create(test_size, CV_32FC1); + labels_predict.create(1, test_size.height, CV_32SC1); + const int max_number_test = test_size.height / K - 1; + row_idx = 0; + for(int i = 0; i < K; i++ ) + { + Mat center_row_header = samples.row(row_idx); + center_row_header.setTo(0); + int nchannel = center_row_header.channels(); + for(int j = 0; j < nchannel; j++) + { + center_row_header.at(0, i * nchannel + j) = 500.0; + } + labels_predict.at(0, row_idx) = i; + for(int j = 0; (j < max_number_test) || + (i == K - 1 && j < max_number_test + test_size.height % K); j ++) + { + Mat cur_row_header = samples.row(row_idx + 1 + j); + center_row_header.copyTo(cur_row_header); + Mat tmpmat = randomMat(rng, cur_row_header.size(), cur_row_header.type(), 1, 100, false); + cur_row_header += tmpmat; + labels_predict.at(0, row_idx + 1 + j) = i; + } + row_idx += 1 + max_number_test; + } + labels_predict.convertTo(labels_predict, CV_32FC1); + } +}; +TEST_P(SVM_OCL, Accuracy) +{ + CvSVMParams params; + params.degree = 0.4; + params.gamma = 1; + params.coef0 = 1; + params.C = 1; + params.nu = 0.5; + params.p = 1; + params.svm_type = svm_type; + params.kernel_type = kernel_type; + + params.term_crit = cvTermCriteria(CV_TERMCRIT_ITER, 1000, 0.001); + + CvSVM SVM; + SVM.train(src, labels, Mat(), Mat(), params); + + cv::ocl::CvSVM_OCL SVM_OCL; + SVM_OCL.train(src, labels, Mat(), Mat(), params); + + int c = SVM.get_support_vector_count(); + int c1 = SVM_OCL.get_support_vector_count(); + + Mat sv(c, MHEIGHT, CV_32FC1); + Mat sv_ocl(c1, MHEIGHT, CV_32FC1); + for(int i = 0; i < c; i++) + { + const float* v = SVM.get_support_vector(i); + + for(int j = 0; j < MHEIGHT; j++) + { + sv.at(i, j) = v[j]; + } + } + for(int i = 0; i < c1; i++) + { + const float* v_ocl = SVM_OCL.get_support_vector(i); + + for(int j = 0; j < MHEIGHT; j++) + { + sv_ocl.at(i, j) = v_ocl[j]; + } + } + cv::BFMatcher matcher(cv::NORM_L2); + std::vector matches; + matcher.match(sv, sv_ocl, matches); + int count = 0; + + for(std::vector::iterator itr = matches.begin(); itr != matches.end(); itr++) + { + if((*itr).distance < 0.1) + { + count ++; + } + } + if(c != 0) + { + float matchedRatio = (float)count / c; + EXPECT_GT(matchedRatio, 0.95); + } + if(c != 0) + { + CvMat *result = cvCreateMat(1, samples.rows, CV_32FC1); + CvMat test_samples = samples; + + CvMat *result_ocl = cvCreateMat(1, samples.rows, CV_32FC1); + + SVM.predict(&test_samples, result); + + SVM_OCL.predict(&test_samples, result_ocl); + + int true_resp = 0, true_resp_ocl = 0; + for (int i = 0; i < samples.rows; i++) + { + if (result->data.fl[i] == labels_predict.at(0, i)) + { + true_resp++; + } + } + float matchedRatio = (float)true_resp / samples.rows; + + for (int i = 0; i < samples.rows; i++) + { + if (result_ocl->data.fl[i] == labels_predict.at(0, i)) + { + true_resp_ocl++; + } + } + float matchedRatio_ocl = (float)true_resp_ocl / samples.rows; + + if(matchedRatio != 0 && true_resp_ocl < true_resp) + { + EXPECT_NEAR(matchedRatio_ocl, matchedRatio, 0.03); + } + } +} +INSTANTIATE_TEST_CASE_P(OCL_ML, SVM_OCL, testing::Combine( + Values(CvSVM::LINEAR, CvSVM::POLY, CvSVM::RBF, CvSVM::SIGMOID), + Values(CvSVM::C_SVC, CvSVM::NU_SVC, CvSVM::ONE_CLASS, CvSVM::EPS_SVR, CvSVM::NU_SVR), + Values(2, 3, 4) + )); +#endif // HAVE_OPENCL