From a13862e9246765fb5ce5062cceeae05b46bb6002 Mon Sep 17 00:00:00 2001 From: Yuhel Tanaka Date: Fri, 6 Sep 2019 03:10:37 +0900 Subject: [PATCH 01/11] Fix issue #6450 --- modules/videoio/src/cap_avfoundation.mm | 688 +++++++++++++----------- 1 file changed, 379 insertions(+), 309 deletions(-) diff --git a/modules/videoio/src/cap_avfoundation.mm b/modules/videoio/src/cap_avfoundation.mm index 4a27f6673a..12636496af 100644 --- a/modules/videoio/src/cap_avfoundation.mm +++ b/modules/videoio/src/cap_avfoundation.mm @@ -35,6 +35,10 @@ #import #import +#define CV_CAP_MODE_BGR CV_FOURCC_MACRO('B','G','R','3') +#define CV_CAP_MODE_RGB CV_FOURCC_MACRO('R','G','B','3') +#define CV_CAP_MODE_GRAY CV_FOURCC_MACRO('G','R','E','Y') +#define CV_CAP_MODE_YUYV CV_FOURCC_MACRO('Y', 'U', 'Y', 'V') /********************** Declaration of class headers ************************/ @@ -125,37 +129,36 @@ class CvCaptureCAM : public CvCapture { *****************************************************************************/ class CvCaptureFile : public CvCapture { - public: +public: + CvCaptureFile(const char* filename) ; + ~CvCaptureFile(); + virtual bool grabFrame(); + virtual IplImage* retrieveFrame(int); + virtual double getProperty(int property_id) const; + virtual bool setProperty(int property_id, double value); + virtual int didStart(); +private: + AVAsset *mAsset; + AVAssetTrack *mAssetTrack; + AVAssetReader *mAssetReader; + AVAssetReaderTrackOutput *mTrackOutput; - CvCaptureFile(const char* filename) ; - ~CvCaptureFile(); - virtual bool grabFrame(); - virtual IplImage* retrieveFrame(int); - virtual IplImage* queryFrame(); - virtual double getProperty(int property_id) const; - virtual bool setProperty(int property_id, double value); - virtual int didStart(); + CMSampleBufferRef mCurrentSampleBuffer; + CVImageBufferRef mGrabbedPixels; + IplImage *mDeviceImage; + uint8_t *mOutImagedata; + IplImage *mOutImage; + size_t currSize; + uint32_t mMode; + int mFormat; - private: + bool setupReadingAt(CMTime position); + IplImage* retrieveFramePixelBuffer(); - AVAssetReader *mMovieReader; - char* imagedata; - IplImage* image; - char* bgr_imagedata; - IplImage* bgr_image; - size_t currSize; + CMTime mFrameTimestamp; + size_t mFrameNum; - IplImage* retrieveFramePixelBuffer(); - double getFPS(); - - int movieWidth; - int movieHeight; - double movieFPS; - double currentFPS; - double movieDuration; - int changedPos; - - int started; + int started; }; @@ -766,216 +769,318 @@ fromConnection:(AVCaptureConnection *)connection{ *****************************************************************************/ CvCaptureFile::CvCaptureFile(const char* filename) { + NSAutoreleasePool *localpool = [[NSAutoreleasePool alloc] init]; - NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init]; - - mMovieReader = nil; - image = NULL; - bgr_image = NULL; - imagedata = NULL; - bgr_imagedata = NULL; + mAsset = nil; + mAssetTrack = nil; + mAssetReader = nil; + mTrackOutput = nil; + mDeviceImage = NULL; + mOutImage = NULL; + mOutImagedata = NULL; currSize = 0; - - movieWidth = 0; - movieHeight = 0; - movieFPS = 0; - currentFPS = 0; - movieDuration = 0; - changedPos = 0; + mMode = CV_CAP_MODE_BGR; + mFormat = CV_8UC3; + mCurrentSampleBuffer = NULL; + mGrabbedPixels = NULL; + mFrameTimestamp = kCMTimeZero; + mFrameNum = 0; started = 0; - AVURLAsset *asset = [AVURLAsset URLAssetWithURL: - [NSURL fileURLWithPath: [NSString stringWithUTF8String:filename]] - options:nil]; + mAsset = [[AVAsset assetWithURL:[NSURL fileURLWithPath: @(filename)]] retain]; - AVAssetTrack* videoTrack = nil; - NSArray* tracks = [asset tracksWithMediaType:AVMediaTypeVideo]; - if ([tracks count] == 1) - { - videoTrack = [tracks objectAtIndex:0]; - - movieWidth = videoTrack.naturalSize.width; - movieHeight = videoTrack.naturalSize.height; - movieFPS = videoTrack.nominalFrameRate; - - currentFPS = movieFPS; //Debugging !! should be getFPS(); - //Debugging. need to be checked - - // In ms - movieDuration = videoTrack.timeRange.duration.value/videoTrack.timeRange.duration.timescale * 1000; - - started = 1; - NSError* error = nil; - mMovieReader = [[AVAssetReader alloc] initWithAsset:asset error:&error]; - if (error) - NSLog(@"%@", [error localizedDescription]); - - NSDictionary* videoSettings = - [NSDictionary dictionaryWithObject:[NSNumber numberWithUnsignedInt:kCVPixelFormatType_32BGRA] - forKey:(NSString*)kCVPixelBufferPixelFormatTypeKey]; - - [mMovieReader addOutput:[AVAssetReaderTrackOutput - assetReaderTrackOutputWithTrack:videoTrack - outputSettings:videoSettings]]; - [mMovieReader startReading]; + if ( mAsset == nil ) { + fprintf(stderr, "OpenCV: Couldn't read movie file \"%s\"\n", filename); + [localpool drain]; + started = 0; + return; } - /* - // Asynchronously open the video in another thread. Always fail. - [asset loadValuesAsynchronouslyForKeys:[NSArray arrayWithObject:@"tracks"] completionHandler: - ^{ - // The completion block goes here. - dispatch_async(dispatch_get_main_queue(), - ^{ - AVAssetTrack* ::videoTrack = nil; - NSArray* ::tracks = [asset tracksWithMediaType:AVMediaTypeVideo]; - if ([tracks count] == 1) - { - videoTrack = [tracks objectAtIndex:0]; + NSArray *tracks = [mAsset tracksWithMediaType:AVMediaTypeVideo]; + if ([tracks count] == 0) { + fprintf(stderr, "OpenCV: Couldn't read video stream from file \"%s\"\n", filename); + [localpool drain]; + started = 0; + return; + } + + mAssetTrack = [tracks[0] retain]; + + if ( ! setupReadingAt(kCMTimeZero) ) { + fprintf(stderr, "OpenCV: Couldn't read movie file \"%s\"\n", filename); + [localpool drain]; + started = 0; + return; + } - movieWidth = videoTrack.naturalSize.width; - movieHeight = videoTrack.naturalSize.height; - movieFPS = videoTrack.nominalFrameRate; - currentFPS = movieFPS; //Debugging !! should be getFPS(); - //Debugging. need to be checked - movieDuration = videoTrack.timeRange.duration.value/videoTrack.timeRange.duration.timescale * 1000; started = 1; - - NSError* ::error = nil; - // mMovieReader is a member variable - mMovieReader = [[AVAssetReader alloc] initWithAsset:asset error:&error]; - if (error) - NSLog(@"%@", [error localizedDescription]); - - NSDictionary* ::videoSettings = - [NSDictionary dictionaryWithObject:[NSNumber numberWithUnsignedInt:kCVPixelFormatType_32BGRA] -forKey:(NSString*)kCVPixelBufferPixelFormatTypeKey]; - -[mMovieReader addOutput:[AVAssetReaderTrackOutput -assetReaderTrackOutputWithTrack:videoTrack -outputSettings:videoSettings]]; -[mMovieReader startReading]; -} -}); - -}]; - */ - -[localpool drain]; + [localpool drain]; } CvCaptureFile::~CvCaptureFile() { + NSAutoreleasePool *localpool = [[NSAutoreleasePool alloc] init]; + + free(mOutImagedata); + cvReleaseImage(&mOutImage); + cvReleaseImage(&mDeviceImage); + [mAssetReader release]; + [mTrackOutput release]; + [mAssetTrack release]; + [mAsset release]; + CVBufferRelease(mGrabbedPixels); + if ( mCurrentSampleBuffer ) { + CFRelease(mCurrentSampleBuffer); + } - NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init]; - if (imagedata != NULL) free(imagedata); - if (bgr_imagedata != NULL) free(bgr_imagedata); - cvReleaseImage(&image); - cvReleaseImage(&bgr_image); - [mMovieReader release]; [localpool drain]; } +bool CvCaptureFile::setupReadingAt(CMTime position) { + if (mAssetReader) { + if (mAssetReader.status == AVAssetReaderStatusReading) { + [mAssetReader cancelReading]; + } + [mAssetReader release]; + mAssetReader = nil; + } + if (mTrackOutput) { + [mTrackOutput release]; + mTrackOutput = nil; + } + + // Capture in a pixel format that can be converted efficiently to the output mode. + OSType pixelFormat; + if (mMode == CV_CAP_MODE_BGR || mMode == CV_CAP_MODE_RGB) { + pixelFormat = kCVPixelFormatType_32BGRA; + mFormat = CV_8UC3; + } else if (mMode == CV_CAP_MODE_GRAY) { + pixelFormat = kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange; + mFormat = CV_8UC1; + } else if (mMode == CV_CAP_MODE_YUYV) { + pixelFormat = kCVPixelFormatType_422YpCbCr8; + mFormat = CV_8UC2; + } else { + fprintf(stderr, "VIDEOIO ERROR: AVF Mac: Unsupported mode: %d\n", mMode); + return false; + } + + NSDictionary *settings = + @{ + (id)kCVPixelBufferPixelFormatTypeKey: @(pixelFormat) + }; + mTrackOutput = [[AVAssetReaderTrackOutput assetReaderTrackOutputWithTrack: mAssetTrack + outputSettings: settings] retain]; + + if ( !mTrackOutput ) { + fprintf(stderr, "OpenCV: error in [AVAssetReaderTrackOutput assetReaderTrackOutputWithTrack:outputSettings:]\n"); + return false; + } + + NSError *error = nil; + mAssetReader = [[AVAssetReader assetReaderWithAsset: mAsset + error: &error] retain]; + if ( error ) { + fprintf(stderr, "OpenCV: error in [AVAssetReader assetReaderWithAsset:error:]\n"); + NSLog(@"OpenCV: %@", error.localizedDescription); + return false; + } + + mAssetReader.timeRange = CMTimeRangeMake(position, kCMTimePositiveInfinity); + mFrameTimestamp = position; + mFrameNum = round((mFrameTimestamp.value * mAssetTrack.nominalFrameRate) / double(mFrameTimestamp.timescale)); + [mAssetReader addOutput: mTrackOutput]; + return [mAssetReader startReading]; +} + int CvCaptureFile::didStart() { return started; } bool CvCaptureFile::grabFrame() { + NSAutoreleasePool *localpool = [[NSAutoreleasePool alloc] init]; - //everything is done in queryFrame; - currentFPS = movieFPS; - return 1; - - - /* - double t1 = getProperty(CV_CAP_PROP_POS_MSEC); - [mCaptureSession stepForward]; - double t2 = getProperty(CV_CAP_PROP_POS_MSEC); - if (t2>t1 && !changedPos) { - currentFPS = 1000.0/(t2-t1); - } else { - currentFPS = movieFPS; - } - changedPos = 0; - - */ + CVBufferRelease(mGrabbedPixels); + if ( mCurrentSampleBuffer ) { + CFRelease(mCurrentSampleBuffer); + } + mCurrentSampleBuffer = [mTrackOutput copyNextSampleBuffer]; + mGrabbedPixels = CMSampleBufferGetImageBuffer(mCurrentSampleBuffer); + CVBufferRetain(mGrabbedPixels); + mFrameTimestamp = CMSampleBufferGetOutputPresentationTimeStamp(mCurrentSampleBuffer); + mFrameNum++; + bool isReading = (mAssetReader.status == AVAssetReaderStatusReading); + [localpool drain]; + return isReading; } - IplImage* CvCaptureFile::retrieveFramePixelBuffer() { - NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init]; - - if (mMovieReader.status != AVAssetReaderStatusReading){ - - return NULL; + if ( ! mGrabbedPixels ) { + return 0; } + NSAutoreleasePool *localpool = [[NSAutoreleasePool alloc] init]; - AVAssetReaderOutput * output = [mMovieReader.outputs objectAtIndex:0]; - CMSampleBufferRef sampleBuffer = [output copyNextSampleBuffer]; - if (!sampleBuffer) { - [localpool drain]; - return NULL; - } - CVPixelBufferRef frame = CMSampleBufferGetImageBuffer(sampleBuffer); - CVPixelBufferRef pixels = CVBufferRetain(frame); + CVPixelBufferLockBaseAddress(mGrabbedPixels, 0); + void *baseaddress; + size_t width, height, rowBytes; - CVPixelBufferLockBaseAddress(pixels, 0); - - uint32_t* baseaddress = (uint32_t*)CVPixelBufferGetBaseAddress(pixels); - size_t width = CVPixelBufferGetWidth(pixels); - size_t height = CVPixelBufferGetHeight(pixels); - size_t rowBytes = CVPixelBufferGetBytesPerRow(pixels); - - if (rowBytes != 0) { - - if (currSize != rowBytes*height*sizeof(char)) { - currSize = rowBytes*height*sizeof(char); - if (imagedata != NULL) free(imagedata); - if (bgr_imagedata != NULL) free(bgr_imagedata); - imagedata = (char*)malloc(currSize); - bgr_imagedata = (char*)malloc(currSize); - } - - memcpy(imagedata, baseaddress, currSize); - - if (image == NULL) { - image = cvCreateImageHeader(cvSize((int)width,(int)height), IPL_DEPTH_8U, 4); - } - - image->width = (int)width; - image->height = (int)height; - image->nChannels = 4; - image->depth = IPL_DEPTH_8U; - image->widthStep = (int)rowBytes; - image->imageData = imagedata; - image->imageSize = (int)currSize; - - - if (bgr_image == NULL) { - bgr_image = cvCreateImageHeader(cvSize((int)width,(int)height), IPL_DEPTH_8U, 3); - } - - bgr_image->width = (int)width; - bgr_image->height = (int)height; - bgr_image->nChannels = 3; - bgr_image->depth = IPL_DEPTH_8U; - bgr_image->widthStep = (int)rowBytes; - bgr_image->imageData = bgr_imagedata; - bgr_image->imageSize = (int)currSize; - - cvCvtColor(image, bgr_image,CV_BGRA2BGR); + OSType pixelFormat = CVPixelBufferGetPixelFormatType(mGrabbedPixels); + if (CVPixelBufferIsPlanar(mGrabbedPixels)) { + baseaddress = CVPixelBufferGetBaseAddressOfPlane(mGrabbedPixels, 0); + width = CVPixelBufferGetWidthOfPlane(mGrabbedPixels, 0); + height = CVPixelBufferGetHeightOfPlane(mGrabbedPixels, 0); + rowBytes = CVPixelBufferGetBytesPerRowOfPlane(mGrabbedPixels, 0); + } else { + baseaddress = CVPixelBufferGetBaseAddress(mGrabbedPixels); + width = CVPixelBufferGetWidth(mGrabbedPixels); + height = CVPixelBufferGetHeight(mGrabbedPixels); + rowBytes = CVPixelBufferGetBytesPerRow(mGrabbedPixels); } - CVPixelBufferUnlockBaseAddress(pixels, 0); - CVBufferRelease(pixels); - CMSampleBufferInvalidate(sampleBuffer); - CFRelease(sampleBuffer); + if ( rowBytes == 0 ) { + fprintf(stderr, "OpenCV: error: rowBytes == 0\n"); + CVPixelBufferUnlockBaseAddress(mGrabbedPixels, 0); + CVBufferRelease(mGrabbedPixels); + mGrabbedPixels = NULL; + return 0; + } + + int outChannels; + if (mMode == CV_CAP_MODE_BGR || mMode == CV_CAP_MODE_RGB) { + outChannels = 3; + } else if (mMode == CV_CAP_MODE_GRAY) { + outChannels = 1; + } else if (mMode == CV_CAP_MODE_YUYV) { + outChannels = 2; + } else { + fprintf(stderr, "VIDEOIO ERROR: AVF Mac: Unsupported mode: %d\n", mMode); + CVPixelBufferUnlockBaseAddress(mGrabbedPixels, 0); + CVBufferRelease(mGrabbedPixels); + mGrabbedPixels = NULL; + return 0; + } + + if ( currSize != width*outChannels*height ) { + currSize = width*outChannels*height; + free(mOutImagedata); + mOutImagedata = reinterpret_cast(malloc(currSize)); + } + + if (mOutImage == NULL) { + mOutImage = cvCreateImageHeader(cvSize((int)width,(int)height), IPL_DEPTH_8U, outChannels); + } + mOutImage->width = int(width); + mOutImage->height = int(height); + mOutImage->nChannels = outChannels; + mOutImage->depth = IPL_DEPTH_8U; + mOutImage->widthStep = int(width*outChannels); + mOutImage->imageData = reinterpret_cast(mOutImagedata); + mOutImage->imageSize = int(currSize); + + int deviceChannels; + int cvtCode; + + if ( pixelFormat == kCVPixelFormatType_32BGRA ) { + deviceChannels = 4; + + if (mMode == CV_CAP_MODE_BGR) { + cvtCode = CV_BGRA2BGR; + } else if (mMode == CV_CAP_MODE_RGB) { + cvtCode = CV_BGRA2RGB; + } else if (mMode == CV_CAP_MODE_GRAY) { + cvtCode = CV_BGRA2GRAY; + } else { + CVPixelBufferUnlockBaseAddress(mGrabbedPixels, 0); + CVBufferRelease(mGrabbedPixels); + mGrabbedPixels = NULL; + fprintf(stderr, "OpenCV: unsupported pixel conversion mode\n"); + return 0; + } + } else if ( pixelFormat == kCVPixelFormatType_24RGB ) { + deviceChannels = 3; + + if (mMode == CV_CAP_MODE_BGR) { + cvtCode = CV_RGB2BGR; + } else if (mMode == CV_CAP_MODE_RGB) { + cvtCode = 0; + } else if (mMode == CV_CAP_MODE_GRAY) { + cvtCode = CV_RGB2GRAY; + } else { + CVPixelBufferUnlockBaseAddress(mGrabbedPixels, 0); + CVBufferRelease(mGrabbedPixels); + mGrabbedPixels = NULL; + fprintf(stderr, "OpenCV: unsupported pixel conversion mode\n"); + return 0; + } + } else if ( pixelFormat == kCVPixelFormatType_422YpCbCr8 ) { // 422 (2vuy, UYVY) + deviceChannels = 2; + + if (mMode == CV_CAP_MODE_BGR) { + cvtCode = CV_YUV2BGR_UYVY; + } else if (mMode == CV_CAP_MODE_RGB) { + cvtCode = CV_YUV2RGB_UYVY; + } else if (mMode == CV_CAP_MODE_GRAY) { + cvtCode = CV_YUV2GRAY_UYVY; + } else if (mMode == CV_CAP_MODE_YUYV) { + cvtCode = -1; // Copy + } else { + CVPixelBufferUnlockBaseAddress(mGrabbedPixels, 0); + CVBufferRelease(mGrabbedPixels); + mGrabbedPixels = NULL; + fprintf(stderr, "OpenCV: unsupported pixel conversion mode\n"); + return 0; + } + } else if ( pixelFormat == kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange || // 420v + pixelFormat == kCVPixelFormatType_420YpCbCr8BiPlanarFullRange ) { // 420f + height = height * 3 / 2; + deviceChannels = 1; + + if (mMode == CV_CAP_MODE_BGR) { + cvtCode = CV_YUV2BGR_YV12; + } else if (mMode == CV_CAP_MODE_RGB) { + cvtCode = CV_YUV2RGB_YV12; + } else if (mMode == CV_CAP_MODE_GRAY) { + cvtCode = CV_YUV2GRAY_420; + } else { + CVPixelBufferUnlockBaseAddress(mGrabbedPixels, 0); + CVBufferRelease(mGrabbedPixels); + mGrabbedPixels = NULL; + fprintf(stderr, "OpenCV: unsupported pixel conversion mode\n"); + return 0; + } + } else { + fprintf(stderr, "OpenCV: unsupported pixel format '%s'\n", pixelFormat); + CVPixelBufferUnlockBaseAddress(mGrabbedPixels, 0); + CVBufferRelease(mGrabbedPixels); + mGrabbedPixels = NULL; + return 0; + } + + if (mDeviceImage == NULL) { + mDeviceImage = cvCreateImageHeader(cvSize(int(width),int(height)), IPL_DEPTH_8U, deviceChannels); + } + mDeviceImage->width = int(width); + mDeviceImage->height = int(height); + mDeviceImage->nChannels = deviceChannels; + mDeviceImage->depth = IPL_DEPTH_8U; + mDeviceImage->widthStep = int(rowBytes); + mDeviceImage->imageData = reinterpret_cast(baseaddress); + mDeviceImage->imageSize = int(rowBytes*height); + + if (cvtCode == -1) { + cv::cvarrToMat(mDeviceImage).copyTo(cv::cvarrToMat(mOutImage)); + } else { + cvCvtColor(mDeviceImage, mOutImage, cvtCode); + } + + CVPixelBufferUnlockBaseAddress(mGrabbedPixels, 0); [localpool drain]; - return bgr_image; + + return mOutImage; } @@ -983,123 +1088,88 @@ IplImage* CvCaptureFile::retrieveFrame(int) { return retrieveFramePixelBuffer(); } -IplImage* CvCaptureFile::queryFrame() { - grabFrame(); - return retrieveFrame(0); +double CvCaptureFile::getProperty(int property_id) const{ + if (mAsset == nil) return 0; + + CMTime t; + + switch (property_id) { + case CV_CAP_PROP_POS_MSEC: + return mFrameTimestamp.value * 1000.0 / mFrameTimestamp.timescale; + case CV_CAP_PROP_POS_FRAMES: + return mAssetTrack.nominalFrameRate > 0 ? mFrameNum : 0; + case CV_CAP_PROP_POS_AVI_RATIO: + t = [mAsset duration]; + return (mFrameTimestamp.value * t.timescale) / double(mFrameTimestamp.timescale * t.value); + case CV_CAP_PROP_FRAME_WIDTH: + return mAssetTrack.naturalSize.width; + case CV_CAP_PROP_FRAME_HEIGHT: + return mAssetTrack.naturalSize.height; + case CV_CAP_PROP_FPS: + return mAssetTrack.nominalFrameRate; + case CV_CAP_PROP_FRAME_COUNT: + t = [mAsset duration]; + return round((t.value * mAssetTrack.nominalFrameRate) / double(t.timescale)); + case CV_CAP_PROP_FORMAT: + return mFormat; + case CV_CAP_PROP_FOURCC: + return mMode; + default: + break; + } + + return 0; } -double CvCaptureFile::getFPS() { +bool CvCaptureFile::setProperty(int property_id, double value) { + if (mAsset == nil) return false; - /* - if (mCaptureSession == nil) return 0; - NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init]; - double now = getProperty(CV_CAP_PROP_POS_MSEC); - double retval = 0; - if (now == 0) { - [mCaptureSession stepForward]; - double t2 = getProperty(CV_CAP_PROP_POS_MSEC); - [mCaptureSession stepBackward]; - retval = 1000.0 / (t2-now); - } else { - [mCaptureSession stepBackward]; - double t2 = getProperty(CV_CAP_PROP_POS_MSEC); - [mCaptureSession stepForward]; - retval = 1000.0 / (now-t2); - } - [localpool drain]; - return retval; - */ - return 30.0; //TODO: Debugging -} + NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init]; -double CvCaptureFile::getProperty(int /*property_id*/) const{ + bool retval = false; + CMTime t; - /* - if (mCaptureSession == nil) return 0; - - NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init]; - - double retval; - QTTime t; - - switch (property_id) { - case CV_CAP_PROP_POS_MSEC: - [[mCaptureSession attributeForKey:QTMovieCurrentTimeAttribute] getValue:&t]; - retval = t.timeValue * 1000.0 / t.timeScale; - break; - case CV_CAP_PROP_POS_FRAMES: - retval = movieFPS * getProperty(CV_CAP_PROP_POS_MSEC) / 1000; - break; - case CV_CAP_PROP_POS_AVI_RATIO: - retval = (getProperty(CV_CAP_PROP_POS_MSEC)) / (movieDuration ); - break; - case CV_CAP_PROP_FRAME_WIDTH: - retval = movieWidth; - break; - case CV_CAP_PROP_FRAME_HEIGHT: - retval = movieHeight; - break; - case CV_CAP_PROP_FPS: - retval = currentFPS; - break; - case CV_CAP_PROP_FOURCC: - default: - retval = 0; - } - - [localpool drain]; - return retval; - */ - return 1.0; //Debugging -} - -bool CvCaptureFile::setProperty(int /*property_id*/, double /*value*/) { - - /* - if (mCaptureSession == nil) return false; - - NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init]; - - bool retval = false; - QTTime t; - - double ms; - - switch (property_id) { - case CV_CAP_PROP_POS_MSEC: - [[mCaptureSession attributeForKey:QTMovieCurrentTimeAttribute] getValue:&t]; - t.timeValue = value * t.timeScale / 1000; - [mCaptureSession setCurrentTime:t]; - changedPos = 1; - retval = true; - break; - case CV_CAP_PROP_POS_FRAMES: - ms = (value*1000.0 -5)/ currentFPS; - retval = setProperty(CV_CAP_PROP_POS_MSEC, ms); - break; - case CV_CAP_PROP_POS_AVI_RATIO: - ms = value * movieDuration; - retval = setProperty(CV_CAP_PROP_POS_MSEC, ms); - break; - case CV_CAP_PROP_FRAME_WIDTH: - //retval = movieWidth; - break; - case CV_CAP_PROP_FRAME_HEIGHT: - //retval = movieHeight; - break; - case CV_CAP_PROP_FPS: - //etval = currentFPS; - break; - case CV_CAP_PROP_FOURCC: - default: - retval = false; + switch (property_id) { + case CV_CAP_PROP_POS_MSEC: + t = mAsset.duration; + t.value = value * t.timescale / 1000; + retval = setupReadingAt(t); + break; + case CV_CAP_PROP_POS_FRAMES: + retval = mAssetTrack.nominalFrameRate > 0 ? setupReadingAt(CMTimeMake(value, mAssetTrack.nominalFrameRate)) : false; + break; + case CV_CAP_PROP_POS_AVI_RATIO: + t = mAsset.duration; + t.value = round(t.value * value); + retval = setupReadingAt(t); + break; + case CV_CAP_PROP_FOURCC: + uint32_t mode; + mode = cvRound(value); + if (mMode == mode) { + retval = true; + } else { + switch (mode) { + case CV_CAP_MODE_BGR: + case CV_CAP_MODE_RGB: + case CV_CAP_MODE_GRAY: + case CV_CAP_MODE_YUYV: + mMode = mode; + retval = setupReadingAt(mFrameTimestamp); + break; + default: + fprintf(stderr, "VIDEOIO ERROR: AVF iOS: Unsupported mode: %d\n", mode); + retval=false; + break; + } + } + break; + default: + break; } [localpool drain]; - return retval; - */ - return true; } From f05d5888c9c37302e0a0eebf7e7eca65017ebf5f Mon Sep 17 00:00:00 2001 From: StefanBruens Date: Sun, 2 Feb 2020 14:29:04 +0100 Subject: [PATCH 02/11] Merge pull request #16479 from StefanBruens:fix_gles_glx_h_include Fix compilation errors on GLES platforms * Do not include glx.h when using GLES GL/glx.h is included on all LINUX plattforms, which is wrong for a number of reasons: - GL_PERSPECTIVE_CORRECTION_HINT is defined in GL/gl.h, so we want gl.h not glx.h, the latter just includes the former - GL/gl.h is a Desktop GL header, and should not be included on GLES plattforms - GL/gl.h is already included via QtOpenGL -> QtGui/qopengl.h on desktop plattforms This fixes a problem when Qt is compiled with GLES, which is often done on ARM platforms where desktop GL is not or only poorly supported (e.g. slow due to emulation). Fixes part of #9171. * Only set GL_PERSPECTIVE_CORRECTION_HINT when GL version defines it GL_PERSPECTIVE_CORRECTION_HINT does not exist in GLES 2.0/3.x, and has been deprecated in OpenGL 3.0 core profiles. Fixes part of #9171. --- modules/highgui/src/window_QT.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/modules/highgui/src/window_QT.cpp b/modules/highgui/src/window_QT.cpp index 1acd0d1672..e83a442c6a 100644 --- a/modules/highgui/src/window_QT.cpp +++ b/modules/highgui/src/window_QT.cpp @@ -54,9 +54,12 @@ #include #endif +// Get GL_PERSPECTIVE_CORRECTION_HINT definition, not available in GLES 2 or +// OpenGL 3 core profile or later #ifdef HAVE_QT_OPENGL - #if defined Q_WS_X11 /* Qt4 */ || defined Q_OS_LINUX /* Qt5 */ - #include + #if defined Q_WS_X11 /* Qt4 */ || \ + (!defined(QT_OPENGL_ES_2) && defined Q_OS_LINUX) /* Qt5 with desktop OpenGL */ + #include #endif #endif @@ -3226,7 +3229,9 @@ void OpenGlViewPort::updateGl() void OpenGlViewPort::initializeGL() { +#ifdef GL_PERSPECTIVE_CORRECTION_HINT glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); +#endif } void OpenGlViewPort::resizeGL(int w, int h) From 579dcf12bda448a71b6767b882e75f6d5cf3e437 Mon Sep 17 00:00:00 2001 From: Rajkiran Natarajan Date: Mon, 3 Feb 2020 06:39:14 +0000 Subject: [PATCH 03/11] GCC 10 warnings not caught by CMake ignore regex --- cmake/OpenCVUtils.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/OpenCVUtils.cmake b/cmake/OpenCVUtils.cmake index 2ea21bfcec..610f0e6437 100644 --- a/cmake/OpenCVUtils.cmake +++ b/cmake/OpenCVUtils.cmake @@ -402,8 +402,8 @@ endmacro() set(OCV_COMPILER_FAIL_REGEX "argument .* is not valid" # GCC 9+ (including support of unicode quotes) - "command line option .* is valid for .* but not for C\\+\\+" # GNU - "command line option .* is valid for .* but not for C" # GNU + "command[- ]line option .* is valid for .* but not for C\\+\\+" # GNU + "command[- ]line option .* is valid for .* but not for C" # GNU "unrecognized .*option" # GNU "unknown .*option" # Clang "ignoring unknown option" # MSVC From 174022547f78aa8061edc0c8e8296d05e4e007f4 Mon Sep 17 00:00:00 2001 From: Vadim Pisarevsky Date: Mon, 3 Feb 2020 13:22:56 +0300 Subject: [PATCH 04/11] fixed incorrect dump of the pixel format --- modules/videoio/src/cap_avfoundation.mm | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/videoio/src/cap_avfoundation.mm b/modules/videoio/src/cap_avfoundation.mm index 12636496af..5c2c989661 100644 --- a/modules/videoio/src/cap_avfoundation.mm +++ b/modules/videoio/src/cap_avfoundation.mm @@ -1052,7 +1052,9 @@ IplImage* CvCaptureFile::retrieveFramePixelBuffer() { return 0; } } else { - fprintf(stderr, "OpenCV: unsupported pixel format '%s'\n", pixelFormat); + char pfBuf[] = { (char)pixelFormat, (char)(pixelFormat >> 8), + (char)(pixelFormat >> 16), (char)(pixelFormat >> 24), '\0' }; + fprintf(stderr, "OpenCV: unsupported pixel format '%s'\n", pfBuf); CVPixelBufferUnlockBaseAddress(mGrabbedPixels, 0); CVBufferRelease(mGrabbedPixels); mGrabbedPixels = NULL; From 005f38fb453fbcf593ab25fe0fa0c2d87e47862e Mon Sep 17 00:00:00 2001 From: Dmitry Kurtaev Date: Mon, 3 Feb 2020 18:54:59 +0300 Subject: [PATCH 05/11] Fix dnn::ResizeLayer to manage varying input shapes --- modules/dnn/src/layers/resize_layer.cpp | 91 ++++--------------------- modules/dnn/test/test_layers.cpp | 31 +++++++++ 2 files changed, 45 insertions(+), 77 deletions(-) diff --git a/modules/dnn/src/layers/resize_layer.cpp b/modules/dnn/src/layers/resize_layer.cpp index 88ad078fe0..593a896042 100644 --- a/modules/dnn/src/layers/resize_layer.cpp +++ b/modules/dnn/src/layers/resize_layer.cpp @@ -19,7 +19,9 @@ namespace cv { namespace dnn { class ResizeLayerImpl : public ResizeLayer { public: - ResizeLayerImpl(const LayerParams& params) : zoomFactorWidth(0), zoomFactorHeight(0), scaleWidth(0), scaleHeight(0) + ResizeLayerImpl(const LayerParams& params) : zoomFactorWidth(params.get("zoom_factor_x", params.get("zoom_factor", 0))), + zoomFactorHeight(params.get("zoom_factor_y", params.get("zoom_factor", 0))), + scaleWidth(0), scaleHeight(0) { setParamsFrom(params); outWidth = params.get("width", 0); @@ -27,13 +29,10 @@ public: if (params.has("zoom_factor")) { CV_Assert(!params.has("zoom_factor_x") && !params.has("zoom_factor_y")); - zoomFactorWidth = zoomFactorHeight = params.get("zoom_factor"); } else if (params.has("zoom_factor_x") || params.has("zoom_factor_y")) { CV_Assert(params.has("zoom_factor_x") && params.has("zoom_factor_y")); - zoomFactorWidth = params.get("zoom_factor_x"); - zoomFactorHeight = params.get("zoom_factor_y"); } interpolation = params.get("interpolation"); CV_Assert(interpolation == "nearest" || interpolation == "bilinear"); @@ -48,8 +47,8 @@ public: { CV_Assert_N(inputs.size() == 1, inputs[0].size() == 4); outputs.resize(1, inputs[0]); - outputs[0][2] = outHeight > 0 ? outHeight : (outputs[0][2] * zoomFactorHeight); - outputs[0][3] = outWidth > 0 ? outWidth : (outputs[0][3] * zoomFactorWidth); + outputs[0][2] = zoomFactorHeight > 0 ? (outputs[0][2] * zoomFactorHeight) : outHeight; + outputs[0][3] = zoomFactorWidth > 0 ? (outputs[0][3] * zoomFactorWidth) : outWidth; // We can work in-place (do nothing) if input shape == output shape. return (outputs[0][2] == inputs[0][2]) && (outputs[0][3] == inputs[0][3]); } @@ -73,11 +72,8 @@ public: inputs_arr.getMatVector(inputs); outputs_arr.getMatVector(outputs); - if (!outWidth && !outHeight) - { - outHeight = outputs[0].size[2]; - outWidth = outputs[0].size[3]; - } + outHeight = outputs[0].size[2]; + outWidth = outputs[0].size[3]; if (alignCorners && outHeight > 1) scaleHeight = static_cast(inputs[0].size[2] - 1) / (outHeight - 1); else @@ -184,7 +180,7 @@ public: ieLayer.setType("Interp"); ieLayer.getParameters()["pad_beg"] = 0; ieLayer.getParameters()["pad_end"] = 0; - ieLayer.getParameters()["align_corners"] = false; + ieLayer.getParameters()["align_corners"] = alignCorners; } else CV_Error(Error::StsNotImplemented, "Unsupported interpolation: " + interpolation); @@ -208,7 +204,7 @@ public: attrs.pads_begin.push_back(0); attrs.pads_end.push_back(0); attrs.axes = ngraph::AxisSet{2, 3}; - attrs.align_corners = false; + attrs.align_corners = alignCorners; if (interpolation == "nearest") { attrs.mode = "nearest"; @@ -227,7 +223,8 @@ public: #endif // HAVE_DNN_NGRAPH protected: - int outWidth, outHeight, zoomFactorWidth, zoomFactorHeight; + int outWidth, outHeight; + const int zoomFactorWidth, zoomFactorHeight; String interpolation; float scaleWidth, scaleHeight; bool alignCorners; @@ -251,78 +248,18 @@ public: { CV_Assert_N(inputs.size() == 1, inputs[0].size() == 4); outputs.resize(1, inputs[0]); - outputs[0][2] = outHeight > 0 ? outHeight : (1 + zoomFactorHeight * (outputs[0][2] - 1)); - outputs[0][3] = outWidth > 0 ? outWidth : (1 + zoomFactorWidth * (outputs[0][3] - 1)); + outputs[0][2] = zoomFactorHeight > 0 ? (1 + zoomFactorHeight * (outputs[0][2] - 1)) : outHeight; + outputs[0][3] = zoomFactorWidth > 0 ? (1 + zoomFactorWidth * (outputs[0][3] - 1)) : outWidth; // We can work in-place (do nothing) if input shape == output shape. return (outputs[0][2] == inputs[0][2]) && (outputs[0][3] == inputs[0][3]); } - - virtual bool supportBackend(int backendId) CV_OVERRIDE - { -#ifdef HAVE_INF_ENGINE - if (backendId == DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019 - || backendId == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH) - return true; -#endif - return backendId == DNN_BACKEND_OPENCV; - } - - virtual void finalize(InputArrayOfArrays inputs_arr, OutputArrayOfArrays outputs_arr) CV_OVERRIDE - { - std::vector inputs, outputs; - inputs_arr.getMatVector(inputs); - outputs_arr.getMatVector(outputs); - - if (!outWidth && !outHeight) - { - outHeight = outputs[0].size[2]; - outWidth = outputs[0].size[3]; - } - int inpHeight = inputs[0].size[2]; - int inpWidth = inputs[0].size[3]; - scaleHeight = (outHeight > 1) ? (static_cast(inpHeight - 1) / (outHeight - 1)) : 0.f; - scaleWidth = (outWidth > 1) ? (static_cast(inpWidth - 1) / (outWidth - 1)) : 0.f; - } - -#ifdef HAVE_INF_ENGINE - virtual Ptr initInfEngine(const std::vector >&) CV_OVERRIDE - { - InferenceEngine::Builder::Layer ieLayer(name); - ieLayer.setName(name); - ieLayer.setType("Interp"); - ieLayer.getParameters()["pad_beg"] = 0; - ieLayer.getParameters()["pad_end"] = 0; - ieLayer.getParameters()["width"] = outWidth; - ieLayer.getParameters()["height"] = outHeight; - ieLayer.setInputPorts(std::vector(1)); - ieLayer.setOutputPorts(std::vector(1)); - return Ptr(new InfEngineBackendNode(ieLayer)); - } -#endif // HAVE_INF_ENGINE - -#ifdef HAVE_DNN_NGRAPH - virtual Ptr initNgraph(const std::vector >& inputs, - const std::vector >& nodes) CV_OVERRIDE - { - auto& ieInpNode = nodes[0].dynamicCast()->node; - ngraph::op::InterpolateAttrs attrs; - attrs.pads_begin.push_back(0); - attrs.pads_end.push_back(0); - attrs.axes = ngraph::AxisSet{2, 3}; - attrs.mode = "linear"; - std::vector shape = {outHeight, outWidth}; - auto out_shape = std::make_shared(ngraph::element::i64, ngraph::Shape{2}, shape.data()); - auto interp = std::make_shared(ieInpNode, out_shape, attrs); - return Ptr(new InfEngineNgraphNode(interp)); - } -#endif // HAVE_DNN_NGRAPH - }; Ptr InterpLayer::create(const LayerParams& params) { LayerParams lp(params); lp.set("interpolation", "bilinear"); + lp.set("align_corners", true); return Ptr(new InterpLayerImpl(lp)); } diff --git a/modules/dnn/test/test_layers.cpp b/modules/dnn/test/test_layers.cpp index 0d61f7af1b..72b0a555b3 100644 --- a/modules/dnn/test/test_layers.cpp +++ b/modules/dnn/test/test_layers.cpp @@ -1715,4 +1715,35 @@ INSTANTIATE_TEST_CASE_P(/**/, Layer_Test_Eltwise_unequal, Combine( dnnBackendsAndTargets() )); +typedef testing::TestWithParam > Layer_Test_Resize; +TEST_P(Layer_Test_Resize, change_input) +{ + int backendId = get<0>(GetParam()); + int targetId = get<1>(GetParam()); + + Net net; + LayerParams lp; + lp.type = "Resize"; + lp.name = "testLayer"; + lp.set("zoom_factor", 2); + lp.set("interpolation", "nearest"); + net.addLayerToPrev(lp.name, lp.type, lp); + + for (int i = 0; i < 2; ++i) + { + Mat inp(4 + i, 5 + i, CV_8UC3), ref; + randu(inp, 0, 255); + resize(inp, ref, Size(0, 0), 2, 2, INTER_NEAREST); + ref = blobFromImage(ref); + + net.setInput(blobFromImage(inp)); + net.setPreferableBackend(backendId); + net.setPreferableTarget(targetId); + Mat out = net.forward(); + normAssert(out, ref); + } +} + +INSTANTIATE_TEST_CASE_P(/**/, Layer_Test_Resize, dnnBackendsAndTargets()); + }} // namespace From edaf4e0104cb1cf8a1ce2278f95c37436802eec3 Mon Sep 17 00:00:00 2001 From: Tomoaki Teshima Date: Tue, 4 Feb 2020 18:49:19 +0900 Subject: [PATCH 06/11] apply to correct range (entire 3.0 and 4.0 series) --- modules/calib3d/src/ap3p.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/calib3d/src/ap3p.cpp b/modules/calib3d/src/ap3p.cpp index cc48b6dac3..386a4499ef 100644 --- a/modules/calib3d/src/ap3p.cpp +++ b/modules/calib3d/src/ap3p.cpp @@ -46,7 +46,7 @@ void solveQuartic(const double *factors, double *realRoots) { complex sqrt_2m = sqrt(static_cast >(-2 * p4 / 3 + t)); double B_4A = -a3 / (4 * a4); double complex1 = 4 * p4 / 3 + t; -#if defined(__clang__) && defined(__arm__) && (__clang_major__ == 3 || __clang_minor__ == 4) && !defined(__ANDROID__) +#if defined(__clang__) && defined(__arm__) && (__clang_major__ == 3 || __clang_major__ == 4) && !defined(__ANDROID__) // details: https://github.com/opencv/opencv/issues/11135 // details: https://github.com/opencv/opencv/issues/11056 complex complex2 = 2 * q4; From 5c6d319ebcf01f16e4b1c19a1a9ed0dc21838551 Mon Sep 17 00:00:00 2001 From: Vadim Pisarevsky Date: Tue, 4 Feb 2020 14:30:16 +0300 Subject: [PATCH 07/11] Merge pull request #16493 from vpisarev:bordertype_sgbm_doc_fixes * added note about BORDER_TYPE in separable filters; fixed SGBMStereo description * added # to BORDER_ constants to generate hyperlinks --- modules/calib3d/include/opencv2/calib3d.hpp | 6 ++-- modules/imgproc/include/opencv2/imgproc.hpp | 35 +++++++++++---------- 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/modules/calib3d/include/opencv2/calib3d.hpp b/modules/calib3d/include/opencv2/calib3d.hpp index 6275ef0f47..42761967db 100644 --- a/modules/calib3d/include/opencv2/calib3d.hpp +++ b/modules/calib3d/include/opencv2/calib3d.hpp @@ -2216,7 +2216,7 @@ CV_EXPORTS_W void filterSpeckles( InputOutputArray img, double newVal, //! computes valid disparity ROI from the valid ROIs of the rectified images (that are returned by cv::stereoRectify()) CV_EXPORTS_W Rect getValidDisparityROI( Rect roi1, Rect roi2, int minDisparity, int numberOfDisparities, - int SADWindowSize ); + int blockSize ); //! validates disparity using the left-right check. The matrix "cost" should be computed by the stereo correspondence algorithm CV_EXPORTS_W void validateDisparity( InputOutputArray disparity, InputArray cost, @@ -2641,8 +2641,8 @@ public: the smoother the disparity is. P1 is the penalty on the disparity change by plus or minus 1 between neighbor pixels. P2 is the penalty on the disparity change by more than 1 between neighbor pixels. The algorithm requires P2 \> P1 . See stereo_match.cpp sample where some reasonably good - P1 and P2 values are shown (like 8\*number_of_image_channels\*SADWindowSize\*SADWindowSize and - 32\*number_of_image_channels\*SADWindowSize\*SADWindowSize , respectively). + P1 and P2 values are shown (like 8\*number_of_image_channels\*blockSize\*blockSize and + 32\*number_of_image_channels\*blockSize\*blockSize , respectively). @param disp12MaxDiff Maximum allowed difference (in integer pixel units) in the left-right disparity check. Set it to a non-positive value to disable the check. @param preFilterCap Truncation value for the prefiltered image pixels. The algorithm first diff --git a/modules/imgproc/include/opencv2/imgproc.hpp b/modules/imgproc/include/opencv2/imgproc.hpp index 769caef14f..5c82dd658f 100644 --- a/modules/imgproc/include/opencv2/imgproc.hpp +++ b/modules/imgproc/include/opencv2/imgproc.hpp @@ -1409,7 +1409,7 @@ equal to sigmaX, if both sigmas are zeros, they are computed from ksize.width an respectively (see #getGaussianKernel for details); to fully control the result regardless of possible future modifications of all this semantics, it is recommended to specify all of ksize, sigmaX, and sigmaY. -@param borderType pixel extrapolation method, see #BorderTypes +@param borderType pixel extrapolation method, see #BorderTypes. #BORDER_WRAP is not supported. @sa sepFilter2D, filter2D, blur, boxFilter, bilateralFilter, medianBlur */ @@ -1470,7 +1470,7 @@ algorithms, and so on). If you need to compute pixel sums over variable-size win @param anchor anchor point; default value Point(-1,-1) means that the anchor is at the kernel center. @param normalize flag, specifying whether the kernel is normalized by its area or not. -@param borderType border mode used to extrapolate pixels outside of the image, see #BorderTypes +@param borderType border mode used to extrapolate pixels outside of the image, see #BorderTypes. #BORDER_WRAP is not supported. @sa blur, bilateralFilter, GaussianBlur, medianBlur, integral */ CV_EXPORTS_W void boxFilter( InputArray src, OutputArray dst, int ddepth, @@ -1493,7 +1493,7 @@ variance and standard deviation around the neighborhood of a pixel. @param anchor kernel anchor point. The default value of Point(-1, -1) denotes that the anchor is at the kernel center. @param normalize flag, specifying whether the kernel is to be normalized by it's area or not. -@param borderType border mode used to extrapolate pixels outside of the image, see #BorderTypes +@param borderType border mode used to extrapolate pixels outside of the image, see #BorderTypes. #BORDER_WRAP is not supported. @sa boxFilter */ CV_EXPORTS_W void sqrBoxFilter( InputArray src, OutputArray dst, int ddepth, @@ -1516,7 +1516,7 @@ the depth should be CV_8U, CV_16U, CV_16S, CV_32F or CV_64F. @param ksize blurring kernel size. @param anchor anchor point; default value Point(-1,-1) means that the anchor is at the kernel center. -@param borderType border mode used to extrapolate pixels outside of the image, see #BorderTypes +@param borderType border mode used to extrapolate pixels outside of the image, see #BorderTypes. #BORDER_WRAP is not supported. @sa boxFilter, bilateralFilter, GaussianBlur, medianBlur */ CV_EXPORTS_W void blur( InputArray src, OutputArray dst, @@ -1550,7 +1550,7 @@ separate color planes using split and process them individually. the kernel; the anchor should lie within the kernel; default value (-1,-1) means that the anchor is at the kernel center. @param delta optional value added to the filtered pixels before storing them in dst. -@param borderType pixel extrapolation method, see #BorderTypes +@param borderType pixel extrapolation method, see #BorderTypes. #BORDER_WRAP is not supported. @sa sepFilter2D, dft, matchTemplate */ CV_EXPORTS_W void filter2D( InputArray src, OutputArray dst, int ddepth, @@ -1571,7 +1571,7 @@ kernel kernelY. The final result shifted by delta is stored in dst . @param anchor Anchor position within the kernel. The default value \f$(-1,-1)\f$ means that the anchor is at the kernel center. @param delta Value added to the filtered results before storing them. -@param borderType Pixel extrapolation method, see #BorderTypes +@param borderType Pixel extrapolation method, see #BorderTypes. #BORDER_WRAP is not supported. @sa filter2D, Sobel, GaussianBlur, boxFilter, blur */ CV_EXPORTS_W void sepFilter2D( InputArray src, OutputArray dst, int ddepth, @@ -1624,7 +1624,7 @@ The second case corresponds to a kernel of: @param scale optional scale factor for the computed derivative values; by default, no scaling is applied (see #getDerivKernels for details). @param delta optional delta value that is added to the results prior to storing them in dst. -@param borderType pixel extrapolation method, see #BorderTypes +@param borderType pixel extrapolation method, see #BorderTypes. #BORDER_WRAP is not supported. @sa Scharr, Laplacian, sepFilter2D, filter2D, GaussianBlur, cartToPolar */ CV_EXPORTS_W void Sobel( InputArray src, OutputArray dst, int ddepth, @@ -1645,7 +1645,8 @@ Sobel( src, dy, CV_16SC1, 0, 1, 3 ); @param dx output image with first-order derivative in x. @param dy output image with first-order derivative in y. @param ksize size of Sobel kernel. It must be 3. -@param borderType pixel extrapolation method, see #BorderTypes +@param borderType pixel extrapolation method, see #BorderTypes. + Only #BORDER_DEFAULT=#BORDER_REFLECT_101 and #BORDER_REPLICATE are supported. @sa Sobel */ @@ -1673,7 +1674,7 @@ is equivalent to @param scale optional scale factor for the computed derivative values; by default, no scaling is applied (see #getDerivKernels for details). @param delta optional delta value that is added to the results prior to storing them in dst. -@param borderType pixel extrapolation method, see #BorderTypes +@param borderType pixel extrapolation method, see #BorderTypes. #BORDER_WRAP is not supported. @sa cartToPolar */ CV_EXPORTS_W void Scharr( InputArray src, OutputArray dst, int ddepth, @@ -1704,7 +1705,7 @@ details. The size must be positive and odd. @param scale Optional scale factor for the computed Laplacian values. By default, no scaling is applied. See #getDerivKernels for details. @param delta Optional delta value that is added to the results prior to storing them in dst . -@param borderType Pixel extrapolation method, see #BorderTypes +@param borderType Pixel extrapolation method, see #BorderTypes. #BORDER_WRAP is not supported. @sa Sobel, Scharr */ CV_EXPORTS_W void Laplacian( InputArray src, OutputArray dst, int ddepth, @@ -1773,7 +1774,7 @@ of the formulae in the cornerEigenValsAndVecs description. src . @param blockSize Neighborhood size (see the details on #cornerEigenValsAndVecs ). @param ksize Aperture parameter for the Sobel operator. -@param borderType Pixel extrapolation method. See #BorderTypes. +@param borderType Pixel extrapolation method. See #BorderTypes. #BORDER_WRAP is not supported. */ CV_EXPORTS_W void cornerMinEigenVal( InputArray src, OutputArray dst, int blockSize, int ksize = 3, @@ -1796,7 +1797,7 @@ size as src . @param blockSize Neighborhood size (see the details on #cornerEigenValsAndVecs ). @param ksize Aperture parameter for the Sobel operator. @param k Harris detector free parameter. See the formula above. -@param borderType Pixel extrapolation method. See #BorderTypes. +@param borderType Pixel extrapolation method. See #BorderTypes. #BORDER_WRAP is not supported. */ CV_EXPORTS_W void cornerHarris( InputArray src, OutputArray dst, int blockSize, int ksize, double k, @@ -1824,7 +1825,7 @@ The output of the function can be used for robust edge or corner detection. @param dst Image to store the results. It has the same size as src and the type CV_32FC(6) . @param blockSize Neighborhood size (see details below). @param ksize Aperture parameter for the Sobel operator. -@param borderType Pixel extrapolation method. See #BorderTypes. +@param borderType Pixel extrapolation method. See #BorderTypes. #BORDER_WRAP is not supported. @sa cornerMinEigenVal, cornerHarris, preCornerDetect */ @@ -1853,7 +1854,7 @@ The corners can be found as local maximums of the functions, as shown below: @param src Source single-channel 8-bit of floating-point image. @param dst Output image that has the type CV_32F and the same size as src . @param ksize %Aperture size of the Sobel . -@param borderType Pixel extrapolation method. See #BorderTypes. +@param borderType Pixel extrapolation method. See #BorderTypes. #BORDER_WRAP is not supported. */ CV_EXPORTS_W void preCornerDetect( InputArray src, OutputArray dst, int ksize, int borderType = BORDER_DEFAULT ); @@ -2117,7 +2118,7 @@ structuring element is used. Kernel can be created using #getStructuringElement. @param anchor position of the anchor within the element; default value (-1, -1) means that the anchor is at the element center. @param iterations number of times erosion is applied. -@param borderType pixel extrapolation method, see #BorderTypes +@param borderType pixel extrapolation method, see #BorderTypes. #BORDER_WRAP is not supported. @param borderValue border value in case of a constant border @sa dilate, morphologyEx, getStructuringElement */ @@ -2149,7 +2150,7 @@ structuring element is used. Kernel can be created using #getStructuringElement @param anchor position of the anchor within the element; default value (-1, -1) means that the anchor is at the element center. @param iterations number of times dilation is applied. -@param borderType pixel extrapolation method, see #BorderTypes +@param borderType pixel extrapolation method, see #BorderTypes. #BORDER_WRAP is not suported. @param borderValue border value in case of a constant border @sa erode, morphologyEx, getStructuringElement */ @@ -2174,7 +2175,7 @@ CV_8U, CV_16U, CV_16S, CV_32F or CV_64F. @param anchor Anchor position with the kernel. Negative values mean that the anchor is at the kernel center. @param iterations Number of times erosion and dilation are applied. -@param borderType Pixel extrapolation method, see #BorderTypes +@param borderType Pixel extrapolation method, see #BorderTypes. #BORDER_WRAP is not supported. @param borderValue Border value in case of a constant border. The default value has a special meaning. @sa dilate, erode, getStructuringElement From 17fe63446fdd0c45a605467db2a444ee4a7d6ed4 Mon Sep 17 00:00:00 2001 From: ankit6979 Date: Tue, 4 Feb 2020 17:52:18 +0530 Subject: [PATCH 08/11] Merge pull request #16499 from ankit6979:3.4 * Update py_thresholding.markdown * :memo: Update py_thresholding.markdown --- .../py_imgproc/py_thresholding/py_thresholding.markdown | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/py_tutorials/py_imgproc/py_thresholding/py_thresholding.markdown b/doc/py_tutorials/py_imgproc/py_thresholding/py_thresholding.markdown index d192288721..285124d17c 100644 --- a/doc/py_tutorials/py_imgproc/py_thresholding/py_thresholding.markdown +++ b/doc/py_tutorials/py_imgproc/py_thresholding/py_thresholding.markdown @@ -188,7 +188,7 @@ blur = cv.GaussianBlur(img,(5,5),0) # find normalized_histogram, and its cumulative distribution function hist = cv.calcHist([blur],[0],None,[256],[0,256]) -hist_norm = hist.ravel()/hist.max() +hist_norm = hist.ravel()/hist.sum() Q = hist_norm.cumsum() bins = np.arange(256) @@ -199,6 +199,8 @@ thresh = -1 for i in xrange(1,256): p1,p2 = np.hsplit(hist_norm,[i]) # probabilities q1,q2 = Q[i],Q[255]-Q[i] # cum sum of classes + if q1 < 1.e-6 or q2 < 1.e-6: + continue b1,b2 = np.hsplit(bins,[i]) # weights # finding means and variances From f67c8e37d640185a6eb0bda5c006cb6c35d75292 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Tue, 4 Feb 2020 17:14:52 +0300 Subject: [PATCH 09/11] imgproc(resize): drop optimization for channels>4 --- modules/imgproc/src/resize.cpp | 86 +--------------------------------- 1 file changed, 1 insertion(+), 85 deletions(-) diff --git a/modules/imgproc/src/resize.cpp b/modules/imgproc/src/resize.cpp index 7596217b73..43458cc153 100644 --- a/modules/imgproc/src/resize.cpp +++ b/modules/imgproc/src/resize.cpp @@ -1674,93 +1674,9 @@ struct HResizeLinearVecU8_X4 } } } - else if(cn < 9) - { - const int step = 8; - const int len0 = xmax & -step; - for( ; k <= (count - 2); k+=2 ) - { - const uchar *S0 = src[k]; - int *D0 = dst[k]; - const uchar *S1 = src[k+1]; - int *D1 = dst[k+1]; - - for( dx = 0; dx < len0; dx += cn ) - { - v_int16x8 a0 = v_load(alpha+dx*2); - v_int16x8 a1 = v_load(alpha+dx*2 + 8); - v_uint16x8 s0, s1; - v_zip(v_load_expand(S0+xofs[dx]), v_load_expand(S0+xofs[dx]+cn), s0, s1); - v_store(&D0[dx], v_dotprod(v_reinterpret_as_s16(s0), a0)); - v_store(&D0[dx+4], v_dotprod(v_reinterpret_as_s16(s1), a1)); - v_zip(v_load_expand(S1+xofs[dx]), v_load_expand(S1+xofs[dx]+cn), s0, s1); - v_store(&D1[dx], v_dotprod(v_reinterpret_as_s16(s0), a0)); - v_store(&D1[dx+4], v_dotprod(v_reinterpret_as_s16(s1), a1)); - } - } - for( ; k < count; k++ ) - { - const uchar *S = src[k]; - int *D = dst[k]; - for( dx = 0; dx < len0; dx += cn ) - { - v_int16x8 a0 = v_load(alpha+dx*2); - v_int16x8 a1 = v_load(alpha+dx*2 + 8); - v_uint16x8 s0, s1; - v_zip(v_load_expand(S+xofs[dx]), v_load_expand(S+xofs[dx]+cn), s0, s1); - v_store(&D[dx], v_dotprod(v_reinterpret_as_s16(s0), a0)); - v_store(&D[dx+4], v_dotprod(v_reinterpret_as_s16(s1), a1)); - } - } - } else { - const int step = 16; - const int len0 = (xmax - cn) & -step; - for( ; k <= (count - 2); k+=2 ) - { - const uchar *S0 = src[k]; - int *D0 = dst[k]; - const uchar *S1 = src[k+1]; - int *D1 = dst[k+1]; - - for( dx = 0; dx < len0; dx += step ) - { - v_int16x8 a0 = v_load(alpha+dx*2); - v_int16x8 a1 = v_load(alpha+dx*2 + 8); - v_int16x8 a2 = v_load(alpha+dx*2 + 16); - v_int16x8 a3 = v_load(alpha+dx*2 + 24); - v_uint8x16 s01, s23; - v_zip(v_lut(S0, xofs+dx), v_lut(S0+cn, xofs+dx), s01, s23); - v_store(&D0[dx], v_dotprod(v_reinterpret_as_s16(v_expand_low(s01)), a0)); - v_store(&D0[dx+4], v_dotprod(v_reinterpret_as_s16(v_expand_high(s01)), a1)); - v_store(&D0[dx+8], v_dotprod(v_reinterpret_as_s16(v_expand_low(s23)), a2)); - v_store(&D0[dx+12], v_dotprod(v_reinterpret_as_s16(v_expand_high(s23)), a3)); - v_zip(v_lut(S1, xofs+dx), v_lut(S1+cn, xofs+dx), s01, s23); - v_store(&D1[dx], v_dotprod(v_reinterpret_as_s16(v_expand_low(s01)), a0)); - v_store(&D1[dx+4], v_dotprod(v_reinterpret_as_s16(v_expand_high(s01)), a1)); - v_store(&D1[dx+8], v_dotprod(v_reinterpret_as_s16(v_expand_low(s23)), a2)); - v_store(&D1[dx+12], v_dotprod(v_reinterpret_as_s16(v_expand_high(s23)), a3)); - } - } - for( ; k < count; k++ ) - { - const uchar *S = src[k]; - int *D = dst[k]; - for( dx = 0; dx < len0; dx += step ) - { - v_int16x8 a0 = v_load(alpha+dx*2); - v_int16x8 a1 = v_load(alpha+dx*2 + 8); - v_int16x8 a2 = v_load(alpha+dx*2 + 16); - v_int16x8 a3 = v_load(alpha+dx*2 + 24); - v_uint8x16 s01, s23; - v_zip(v_lut(S, xofs+dx), v_lut(S+cn, xofs+dx), s01, s23); - v_store(&D[dx], v_dotprod(v_reinterpret_as_s16(v_expand_low(s01)), a0)); - v_store(&D[dx+4], v_dotprod(v_reinterpret_as_s16(v_expand_high(s01)), a1)); - v_store(&D[dx+8], v_dotprod(v_reinterpret_as_s16(v_expand_low(s23)), a2)); - v_store(&D[dx+12], v_dotprod(v_reinterpret_as_s16(v_expand_high(s23)), a3)); - } - } + return 0; // images with channels >4 are out of optimization scope } return dx; } From 4e4dfffe06f8b2c69169d43018c5ec77654b7771 Mon Sep 17 00:00:00 2001 From: Polina Smolnikova <43805563+rayonnant14@users.noreply.github.com> Date: Tue, 4 Feb 2020 19:31:11 +0300 Subject: [PATCH 10/11] Merge pull request #16491 from rayonnant14:objdetect_decodeMulti_issue * fixed issue with Mat reshape in decodeMulti() * added test for decodeMulti * used assign --- modules/objdetect/src/qrcode.cpp | 3 ++- modules/objdetect/test/test_qrcode.cpp | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/modules/objdetect/src/qrcode.cpp b/modules/objdetect/src/qrcode.cpp index 6ceb6d4ee4..f4a26fb93a 100644 --- a/modules/objdetect/src/qrcode.cpp +++ b/modules/objdetect/src/qrcode.cpp @@ -2304,7 +2304,8 @@ bool QRCodeDetector::decodeMulti( CV_Assert((points.size().width % 4) == 0); vector< vector< Point2f > > src_points ; Mat qr_points = points.getMat(); - for (int i = 0; i < points.size().width ; i += 4) + qr_points = qr_points.reshape(2, 1); + for (int i = 0; i < qr_points.size().width ; i += 4) { vector tempMat = qr_points.colRange(i, i + 4); if (contourArea(tempMat) > 0.0) diff --git a/modules/objdetect/test/test_qrcode.cpp b/modules/objdetect/test/test_qrcode.cpp index d26323ea76..cda5a02311 100644 --- a/modules/objdetect/test/test_qrcode.cpp +++ b/modules/objdetect/test/test_qrcode.cpp @@ -481,6 +481,26 @@ INSTANTIATE_TEST_CASE_P(/**/, Objdetect_QRCode_Close, testing::ValuesIn(qrcode_i INSTANTIATE_TEST_CASE_P(/**/, Objdetect_QRCode_Monitor, testing::ValuesIn(qrcode_images_monitor)); INSTANTIATE_TEST_CASE_P(/**/, Objdetect_QRCode_Multi, testing::ValuesIn(qrcode_images_multiple)); +TEST(Objdetect_QRCode_decodeMulti, decode_regression_16491) +{ +#ifdef HAVE_QUIRC + Mat zero_image = Mat::zeros(256, 256, CV_8UC1); + Point corners_[] = {Point(16, 16), Point(128, 16), Point(128, 128), Point(16, 128), + Point(16, 16), Point(128, 16), Point(128, 128), Point(16, 128)}; + std::vector vec_corners; + int array_size = 8; + vec_corners.assign(corners_, corners_ + array_size); + std::vector decoded_info; + std::vector straight_barcode; + QRCodeDetector vec_qrcode; + EXPECT_NO_THROW(vec_qrcode.decodeMulti(zero_image, vec_corners, decoded_info, straight_barcode)); + + Mat mat_corners(2, 4, CV_32SC2, (void*)&vec_corners[0]); + QRCodeDetector mat_qrcode; + EXPECT_NO_THROW(mat_qrcode.decodeMulti(zero_image, mat_corners, decoded_info, straight_barcode)); +#endif +} + TEST(Objdetect_QRCode_basic, not_found_qrcode) { std::vector corners; From e50acb923e5af82e42ac6997e663d011d41ea544 Mon Sep 17 00:00:00 2001 From: Vadim Pisarevsky Date: Tue, 4 Feb 2020 19:37:33 +0300 Subject: [PATCH 11/11] Merge pull request #16495 from vpisarev:drawing_aa_border_fix * fixed antialiased line rendering to process image border correctly * fixed warning on Windows * imgproc(test): circle drawing regression --- modules/imgproc/src/drawing.cpp | 195 +++++++++++--------------- modules/imgproc/test/test_drawing.cpp | 10 ++ 2 files changed, 93 insertions(+), 112 deletions(-) diff --git a/modules/imgproc/src/drawing.cpp b/modules/imgproc/src/drawing.cpp index eb06a98c11..e9b212d388 100644 --- a/modules/imgproc/src/drawing.cpp +++ b/modules/imgproc/src/drawing.cpp @@ -308,7 +308,7 @@ LineAA( Mat& img, Point2l pt1, Point2l pt2, const void* color ) int nch = img.channels(); uchar* ptr = img.ptr(); size_t step = img.step; - Size2l size(img.size()); + Size2l size0(img.size()), size = size0; if( !((nch == 1 || nch == 3 || nch == 4) && img.depth() == CV_8U) ) { @@ -316,15 +316,8 @@ LineAA( Mat& img, Point2l pt1, Point2l pt2, const void* color ) return; } - pt1.x -= XY_ONE*2; - pt1.y -= XY_ONE*2; - pt2.x -= XY_ONE*2; - pt2.y -= XY_ONE*2; - ptr += img.step*2 + 2*nch; - - size.width = ((size.width - 5) << XY_SHIFT) + 1; - size.height = ((size.height - 5) << XY_SHIFT) + 1; - + size.width <<= XY_SHIFT; + size.height <<= XY_SHIFT; if( !clipLine( size, pt1, pt2 )) return; @@ -403,171 +396,160 @@ LineAA( Mat& img, Point2l pt1, Point2l pt2, const void* color ) if( nch == 3 ) { - #define ICV_PUT_POINT() \ + #define ICV_PUT_POINT(x, y) \ { \ + uchar* tptr = ptr + (x)*3 + (y)*step; \ _cb = tptr[0]; \ _cb += ((cb - _cb)*a + 127)>> 8;\ + _cb += ((cb - _cb)*a + 127)>> 8;\ _cg = tptr[1]; \ _cg += ((cg - _cg)*a + 127)>> 8;\ + _cg += ((cg - _cg)*a + 127)>> 8;\ _cr = tptr[2]; \ _cr += ((cr - _cr)*a + 127)>> 8;\ + _cr += ((cr - _cr)*a + 127)>> 8;\ tptr[0] = (uchar)_cb; \ tptr[1] = (uchar)_cg; \ tptr[2] = (uchar)_cr; \ } if( ax > ay ) { - ptr += (pt1.x >> XY_SHIFT) * 3; + int x = (int)(pt1.x >> XY_SHIFT); - while( ecount >= 0 ) + for( ; ecount >= 0; x++, pt1.y += y_step, scount++, ecount-- ) { - uchar *tptr = ptr + ((pt1.y >> XY_SHIFT) - 1) * step; + if( (unsigned)x >= (unsigned)size0.width ) + continue; + int y = (int)((pt1.y >> XY_SHIFT) - 1); int ep_corr = ep_table[(((scount >= 2) + 1) & (scount | 2)) * 3 + (((ecount >= 2) + 1) & (ecount | 2))]; int a, dist = (pt1.y >> (XY_SHIFT - 5)) & 31; a = (ep_corr * FilterTable[dist + 32] >> 8) & 0xff; - ICV_PUT_POINT(); - ICV_PUT_POINT(); + if( (unsigned)y < (unsigned)size0.height ) + ICV_PUT_POINT(x, y) - tptr += step; a = (ep_corr * FilterTable[dist] >> 8) & 0xff; - ICV_PUT_POINT(); - ICV_PUT_POINT(); + if( (unsigned)(y+1) < (unsigned)size0.height ) + ICV_PUT_POINT(x, y+1) - tptr += step; a = (ep_corr * FilterTable[63 - dist] >> 8) & 0xff; - ICV_PUT_POINT(); - ICV_PUT_POINT(); - - pt1.y += y_step; - ptr += 3; - scount++; - ecount--; + if( (unsigned)(y+2) < (unsigned)size0.height ) + ICV_PUT_POINT(x, y+2) } } else { - ptr += (pt1.y >> XY_SHIFT) * step; + int y = (int)(pt1.y >> XY_SHIFT); - while( ecount >= 0 ) + for( ; ecount >= 0; y++, pt1.x += x_step, scount++, ecount-- ) { - uchar *tptr = ptr + ((pt1.x >> XY_SHIFT) - 1) * 3; - + if( (unsigned)y >= (unsigned)size0.height ) + continue; + int x = (int)((pt1.x >> XY_SHIFT) - 1); int ep_corr = ep_table[(((scount >= 2) + 1) & (scount | 2)) * 3 + (((ecount >= 2) + 1) & (ecount | 2))]; int a, dist = (pt1.x >> (XY_SHIFT - 5)) & 31; a = (ep_corr * FilterTable[dist + 32] >> 8) & 0xff; - ICV_PUT_POINT(); - ICV_PUT_POINT(); + if( (unsigned)x < (unsigned)size0.width ) + ICV_PUT_POINT(x, y) - tptr += 3; a = (ep_corr * FilterTable[dist] >> 8) & 0xff; - ICV_PUT_POINT(); - ICV_PUT_POINT(); + if( (unsigned)(x+1) < (unsigned)size0.width ) + ICV_PUT_POINT(x+1, y) - tptr += 3; a = (ep_corr * FilterTable[63 - dist] >> 8) & 0xff; - ICV_PUT_POINT(); - ICV_PUT_POINT(); - - pt1.x += x_step; - ptr += step; - scount++; - ecount--; + if( (unsigned)(x+2) < (unsigned)size0.width ) + ICV_PUT_POINT(x+2, y) } } #undef ICV_PUT_POINT } else if(nch == 1) { - #define ICV_PUT_POINT() \ + #define ICV_PUT_POINT(x, y) \ { \ + uchar* tptr = ptr + (x) + (y) * step; \ _cb = tptr[0]; \ _cb += ((cb - _cb)*a + 127)>> 8;\ + _cb += ((cb - _cb)*a + 127)>> 8;\ tptr[0] = (uchar)_cb; \ } if( ax > ay ) { - ptr += (pt1.x >> XY_SHIFT); + int x = (int)(pt1.x >> XY_SHIFT); - while( ecount >= 0 ) + for( ; ecount >= 0; x++, pt1.y += y_step, scount++, ecount-- ) { - uchar *tptr = ptr + ((pt1.y >> XY_SHIFT) - 1) * step; + if( (unsigned)x >= (unsigned)size0.width ) + continue; + int y = (int)((pt1.y >> XY_SHIFT) - 1); int ep_corr = ep_table[(((scount >= 2) + 1) & (scount | 2)) * 3 + (((ecount >= 2) + 1) & (ecount | 2))]; int a, dist = (pt1.y >> (XY_SHIFT - 5)) & 31; a = (ep_corr * FilterTable[dist + 32] >> 8) & 0xff; - ICV_PUT_POINT(); - ICV_PUT_POINT(); + if( (unsigned)y < (unsigned)size0.height ) + ICV_PUT_POINT(x, y) - tptr += step; a = (ep_corr * FilterTable[dist] >> 8) & 0xff; - ICV_PUT_POINT(); - ICV_PUT_POINT(); + if( (unsigned)(y+1) < (unsigned)size0.height ) + ICV_PUT_POINT(x, y+1) - tptr += step; a = (ep_corr * FilterTable[63 - dist] >> 8) & 0xff; - ICV_PUT_POINT(); - ICV_PUT_POINT(); - - pt1.y += y_step; - ptr++; - scount++; - ecount--; + if( (unsigned)(y+2) < (unsigned)size0.height ) + ICV_PUT_POINT(x, y+2) } } else { - ptr += (pt1.y >> XY_SHIFT) * step; + int y = (int)(pt1.y >> XY_SHIFT); - while( ecount >= 0 ) + for( ; ecount >= 0; y++, pt1.x += x_step, scount++, ecount-- ) { - uchar *tptr = ptr + ((pt1.x >> XY_SHIFT) - 1); - + if( (unsigned)y >= (unsigned)size0.height ) + continue; + int x = (int)((pt1.x >> XY_SHIFT) - 1); int ep_corr = ep_table[(((scount >= 2) + 1) & (scount | 2)) * 3 + (((ecount >= 2) + 1) & (ecount | 2))]; int a, dist = (pt1.x >> (XY_SHIFT - 5)) & 31; a = (ep_corr * FilterTable[dist + 32] >> 8) & 0xff; - ICV_PUT_POINT(); - ICV_PUT_POINT(); + if( (unsigned)x < (unsigned)size0.width ) + ICV_PUT_POINT(x, y) - tptr++; a = (ep_corr * FilterTable[dist] >> 8) & 0xff; - ICV_PUT_POINT(); - ICV_PUT_POINT(); + if( (unsigned)(x+1) < (unsigned)size0.width ) + ICV_PUT_POINT(x+1, y) - tptr++; a = (ep_corr * FilterTable[63 - dist] >> 8) & 0xff; - ICV_PUT_POINT(); - ICV_PUT_POINT(); - - pt1.x += x_step; - ptr += step; - scount++; - ecount--; + if( (unsigned)(x+2) < (unsigned)size0.width ) + ICV_PUT_POINT(x+2, y) } } #undef ICV_PUT_POINT } else { - #define ICV_PUT_POINT() \ + #define ICV_PUT_POINT(x, y) \ { \ + uchar* tptr = ptr + (x)*4 + (y)*step; \ _cb = tptr[0]; \ _cb += ((cb - _cb)*a + 127)>> 8;\ + _cb += ((cb - _cb)*a + 127)>> 8;\ _cg = tptr[1]; \ _cg += ((cg - _cg)*a + 127)>> 8;\ + _cg += ((cg - _cg)*a + 127)>> 8;\ _cr = tptr[2]; \ _cr += ((cr - _cr)*a + 127)>> 8;\ + _cr += ((cr - _cr)*a + 127)>> 8;\ _ca = tptr[3]; \ _ca += ((ca - _ca)*a + 127)>> 8;\ + _ca += ((ca - _ca)*a + 127)>> 8;\ tptr[0] = (uchar)_cb; \ tptr[1] = (uchar)_cg; \ tptr[2] = (uchar)_cr; \ @@ -575,66 +557,55 @@ LineAA( Mat& img, Point2l pt1, Point2l pt2, const void* color ) } if( ax > ay ) { - ptr += (pt1.x >> XY_SHIFT) * 4; + int x = (int)(pt1.x >> XY_SHIFT); - while( ecount >= 0 ) + for( ; ecount >= 0; x++, pt1.y += y_step, scount++, ecount-- ) { - uchar *tptr = ptr + ((pt1.y >> XY_SHIFT) - 1) * step; + if( (unsigned)x >= (unsigned)size0.width ) + continue; + int y = (int)((pt1.y >> XY_SHIFT) - 1); int ep_corr = ep_table[(((scount >= 2) + 1) & (scount | 2)) * 3 + (((ecount >= 2) + 1) & (ecount | 2))]; int a, dist = (pt1.y >> (XY_SHIFT - 5)) & 31; a = (ep_corr * FilterTable[dist + 32] >> 8) & 0xff; - ICV_PUT_POINT(); - ICV_PUT_POINT(); + if( (unsigned)y < (unsigned)size0.height ) + ICV_PUT_POINT(x, y) - tptr += step; a = (ep_corr * FilterTable[dist] >> 8) & 0xff; - ICV_PUT_POINT(); - ICV_PUT_POINT(); + if( (unsigned)(y+1) < (unsigned)size0.height ) + ICV_PUT_POINT(x, y+1) - tptr += step; a = (ep_corr * FilterTable[63 - dist] >> 8) & 0xff; - ICV_PUT_POINT(); - ICV_PUT_POINT(); - - pt1.y += y_step; - ptr += 4; - scount++; - ecount--; + if( (unsigned)(y+2) < (unsigned)size0.height ) + ICV_PUT_POINT(x, y+2) } } else { - ptr += (pt1.y >> XY_SHIFT) * step; + int y = (int)(pt1.y >> XY_SHIFT); - while( ecount >= 0 ) + for( ; ecount >= 0; y++, pt1.x += x_step, scount++, ecount-- ) { - uchar *tptr = ptr + ((pt1.x >> XY_SHIFT) - 1) * 4; - + if( (unsigned)y >= (unsigned)size0.height ) + continue; + int x = (int)((pt1.x >> XY_SHIFT) - 1); int ep_corr = ep_table[(((scount >= 2) + 1) & (scount | 2)) * 3 + (((ecount >= 2) + 1) & (ecount | 2))]; int a, dist = (pt1.x >> (XY_SHIFT - 5)) & 31; a = (ep_corr * FilterTable[dist + 32] >> 8) & 0xff; - ICV_PUT_POINT(); - ICV_PUT_POINT(); + if( (unsigned)x < (unsigned)size0.width ) + ICV_PUT_POINT(x, y) - tptr += 4; a = (ep_corr * FilterTable[dist] >> 8) & 0xff; - ICV_PUT_POINT(); - ICV_PUT_POINT(); + if( (unsigned)(x+1) < (unsigned)size0.width ) + ICV_PUT_POINT(x+1, y) - tptr += 4; a = (ep_corr * FilterTable[63 - dist] >> 8) & 0xff; - ICV_PUT_POINT(); - ICV_PUT_POINT(); - - pt1.x += x_step; - ptr += step; - scount++; - ecount--; + if( (unsigned)(x+2) < (unsigned)size0.width ) + ICV_PUT_POINT(x+2, y) } } #undef ICV_PUT_POINT diff --git a/modules/imgproc/test/test_drawing.cpp b/modules/imgproc/test/test_drawing.cpp index f18709448c..2796d35ba8 100644 --- a/modules/imgproc/test/test_drawing.cpp +++ b/modules/imgproc/test/test_drawing.cpp @@ -583,4 +583,14 @@ TEST(Drawing, line) ASSERT_THROW(line(mat, Point(1,1),Point(99,99),Scalar(255),0), cv::Exception); } +TEST(Drawing, regression_16308) +{ + Mat_ img(Size(100, 100), (uchar)0); + circle(img, Point(50, 50), 50, 255, 1, LINE_AA); + EXPECT_NE(0, (int)img.at(0, 50)); + EXPECT_NE(0, (int)img.at(50, 0)); + EXPECT_NE(0, (int)img.at(50, 99)); + EXPECT_NE(0, (int)img.at(99, 50)); +} + }} // namespace