private void RunAnalyzeVideo() { Log.Info("Auto3D: Start Video Analysis"); FrameGrabber fg = FrameGrabber.GetInstance(); int maxAnalyzeSteps = 20; int treshold = 5; VideoFormat[] vf = new VideoFormat[maxAnalyzeSteps + 1]; int iStep = 0; while (_run && _bPlaying) { // wait 200 ms for (int i = 0; i < 10; i++) { if (!_bPlaying) // if playing is stopped while we wait then return { return; } Thread.Sleep(20); } System.Drawing.Bitmap image = fg.GetCurrentImage(); if (image != null) { Bitmap fastCompareImage = new Bitmap(96, 96); // set the resolutions the same to avoid cropping due to resolution differences fastCompareImage.SetResolution(image.HorizontalResolution, image.VerticalResolution); //use a graphics object to draw the resized image into the bitmap using (Graphics graphics = Graphics.FromImage(fastCompareImage)) { graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.Bicubic; graphics.DrawImage(image, 0, 0, fastCompareImage.Width, fastCompareImage.Height); } // Lock the bitmap's bits. Rectangle rect = new Rectangle(0, 0, fastCompareImage.Width, fastCompareImage.Height); System.Drawing.Imaging.BitmapData bmpData = fastCompareImage.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format24bppRgb); double similarity = 0; vf[iStep] = VideoFormat.Fmt2D; // assume normal format if (bCheckSideBySide) { similarity = Auto3DAnalyzer.CheckFor3DFormat(bmpData, bmpData.Width / 2, bmpData.Height, true); } if (similarity == -1) // not bright enough for analysis { continue; } if (similarity > 0.925) { vf[iStep] = VideoFormat.Fmt3DSBS; } else { if (bCheckTopAndBottom) { similarity = Auto3DAnalyzer.CheckFor3DFormat(bmpData, bmpData.Width, bmpData.Height / 2, false); } if (similarity == -1) // not bright enough for analysis -> continue { continue; } if (similarity > 0.925) { vf[iStep] = VideoFormat.Fmt3DTAB; } } fastCompareImage.UnlockBits(bmpData); Log.Debug("Similarity: " + similarity + " - " + vf[iStep].ToString()); } if (iStep > 3) { // check if we can make a decision int countNormal = 0; int countSideBySide3D = 0; int countTopBottom3D = 0; for (int i = 0; i <= iStep; i++) { switch (vf[i]) { case VideoFormat.Fmt2D: countNormal++; break; case VideoFormat.Fmt3DSBS: countSideBySide3D++; break; case VideoFormat.Fmt3DTAB: countTopBottom3D++; break; } } Log.Debug("Results(" + iStep + ") - Normal=" + countNormal + " - SBS3D=" + countSideBySide3D + " - TB3D=" + countTopBottom3D); if ((countSideBySide3D >= (countNormal + treshold)) || (countTopBottom3D >= (countNormal + treshold)) || (countSideBySide3D >= countNormal && iStep == maxAnalyzeSteps) || (countTopBottom3D >= countNormal && iStep == maxAnalyzeSteps)) { VideoFormat videoFormat = countTopBottom3D > countSideBySide3D ? VideoFormat.Fmt3DTAB : VideoFormat.Fmt3DSBS; if ((videoFormat == VideoFormat.Fmt3DSBS) || (videoFormat == VideoFormat.Fmt3DTAB)) { if (videoFormat == VideoFormat.Fmt3DTAB) { Log.Info("Auto3D: Video Analysis Finished: Switch TV to TAB 3D"); } else { Log.Info("Auto3D: Video Analysis Finished: Switch TV to SBS 3D"); } if (bConvert3DTo2D) { switch (videoFormat) { case VideoFormat.Fmt3DSBS: GUIGraphicsContext.Render3DMode = GUIGraphicsContext.eRender3DMode.SideBySideTo2D; break; case VideoFormat.Fmt3DTAB: GUIGraphicsContext.Render3DMode = GUIGraphicsContext.eRender3DMode.TopAndBottomTo2D; break; } _currentMode = videoFormat; } else { if (_activeDevice.SwitchFormat(_currentMode, videoFormat)) { switch (videoFormat) { case VideoFormat.Fmt3DSBS: GUIGraphicsContext.Render3DMode = GUIGraphicsContext.eRender3DMode.SideBySide; break; case VideoFormat.Fmt3DTAB: GUIGraphicsContext.Render3DMode = GUIGraphicsContext.eRender3DMode.TopAndBottom; break; } _currentMode = videoFormat; } } UpdateSubtitleRenderFormat(); } else { ManualSelect3DFormat(videoFormat); UpdateSubtitleRenderFormat(); } return; // exit thread } else if ((_currentMode == VideoFormat.Fmt2D) && ((countNormal > countSideBySide3D + treshold) || (countNormal > countTopBottom3D + treshold))) { // current format is normal and video is normal too, we do not need to switch Log.Info("Auto3D: Format is 2D. No switch necessary"); return; // exit thread } else if (_currentMode != VideoFormat.Fmt2D) { // current format 3d and video is 2d, so we must switch back to normal RunSwitchBack(); return; // exit thread } else if (iStep > maxAnalyzeSteps) { // we could not make a decision within the maximum allowed steps Log.Info("Auto3D: Video Analysis failed!"); return; // exit thread } } iStep++; } }
/// <summary> /// Finds the bounds and crops accordingly immediately /// </summary> private void SingleCrop() { bool updateCrop = false; if (LastSwitchedAspectRatio < 0.1f) { LastDetectionResult = false; return; } if (GUIGraphicsContext.RenderBlackImage) { LastDetectionResult = false; return; } Bitmap frame = grabber.GetCurrentImage(); if (frame == null || frame.Height == 0 || frame.Width == 0) { LastDetectionResult = false; return; } Rectangle bounds = new Rectangle(); if (!analyzer.FindBounds(frame, ref bounds)) { LastDetectionResult = false; frame.Dispose(); frame = null; return; } double Hsym = 1.0; double Vsym = 1.0; if (bounds.Left > 20 || ((frame.Width - bounds.Right) > 20 && bounds.Left > 0)) { Hsym = (double)(frame.Width - bounds.Right) / (double)bounds.Left; } if (bounds.Top > 20 || ((frame.Height - bounds.Bottom) > 20 && bounds.Top > 0)) { Vsym = (double)(frame.Height - bounds.Bottom) / (double)bounds.Top; } if (currentSettings.verboseLog) { Log.Debug("ViewModeSwitcher: SingleCrop(), Detected BB -> left: {0}, right: {1}, top: {2}, bottom: {3}, Hsym: {4}, Vsym: {5}", bounds.Left, (frame.Width - bounds.Right), bounds.Top, (frame.Height - bounds.Bottom), Hsym, Vsym); } //Check for symmetry of black bars - asymmetric bars are probably a false detection if ((Hsym > SymLimHigh) || (Hsym < SymLimLow) || (Vsym > SymLimHigh) || (Vsym < SymLimLow)) { if (currentSettings.verboseLog) { Log.Debug("ViewModeSwitcher: SingleCrop(), Symmetry check failed"); } if (NoMatchCropCount < 3) { NoMatchCropCount++; LastDetectionResult = false; } frame.Dispose(); frame = null; return; } //Use the smallest black bar size double cropH = (double)(Math.Min(frame.Width - bounds.Right, bounds.Left)); double cropV = (double)(Math.Min(frame.Height - bounds.Bottom, bounds.Top)); //Check for a close match (within 1% of width/height) to the previous crop values if ((((Math.Abs(cropV - LastRawCropV)) / (double)frame.Height) > 0.01) || (((Math.Abs(cropH - LastRawCropH)) / (double)frame.Width) > 0.01)) { LastRawCropH = cropH; LastRawCropV = cropV; if (NoMatchCropCount < 3) { NoMatchCropCount++; LastDetectionResult = false; } if (currentSettings.verboseLog) { Log.Debug("ViewModeSwitcher: SingleCrop(), No match with last crop : " + NoMatchCropCount); } frame.Dispose(); frame = null; return; } LastRawCropH = cropH; LastRawCropV = cropV; //Work out actual picture aspect ratio double newasp = ((double)frame.Width - cropH * 2) / ((double)frame.Height - cropV * 2); bool checkPBv = false; if (LastAnamorphFactor > 0.0) { //Correction for anamorphic video i.e. when pixel aspect ratio != video aspect ratio. newasp *= LastAnamorphFactor; //Check for letterbox/pillarbox video if (LastSwitchedAspectRatio > 1.68 && LastSwitchedAspectRatio < 1.87) { if ((cropV / (double)frame.Height < 0.02) && newasp > 1.2 && newasp < 1.46) { checkPBv = true; } else if ((cropH / (double)frame.Width < 0.02) && newasp > 2.1 && newasp < 2.57) { checkPBv = true; } } else if (LastSwitchedAspectRatio > 1.25 && LastSwitchedAspectRatio < 1.41) { if ((cropH / (double)frame.Width < 0.02) && newasp > 1.47 && newasp < 1.95) { checkPBv = true; } else if ((cropH / (double)frame.Width < 0.02) && newasp > 2.1 && newasp < 2.57) { checkPBv = true; } } //After this correction, cropH value is in 'real' video pixels, not source pixels cropH *= LastAnamorphFactor; } if (newasp < 1.1 || newasp > 2.7) // faulty crop { if (NoMatchCropCount < 3) { NoMatchCropCount++; LastDetectionResult = false; } frame.Dispose(); frame = null; return; } NoMatchCropCount = 0; if (currentSettings.verboseLog) { Log.Debug("ViewModeSwitcher: SingleCrop(), Video AR: {0}, Cropped AR: {1}", LastSwitchedAspectRatio, newasp); } //Check for 'Pillar Boxed' 4:3 inside 16:9 video, and 'Letter Boxed' 16:9 inside 4:3 video if (CheckRulesPBLB(-newasp, frame.Width, frame.Height, checkPBv)) { if (LastSwitchedGeometry != Geometry.Type.NonLinearStretch) { //NonLinearStretch needs full side bar cropping, other modes don't cropH = overScan; cropV = overScan / LastSwitchedAspectRatio; } else { //Add overscan to NonLinearStretch cropH += overScan; cropV += overScan / LastSwitchedAspectRatio; } if (!isPBorLB) //Only update on first detection { updateCrop = true; } isPBorLB = true; } else //Normal video cropping { //Use overscan cropping if larger than detected black bars cropH = Math.Max(cropH, overScan); cropV = Math.Max(cropV, overScan / LastSwitchedAspectRatio); if (isPBorLB) { if (currentSettings.verboseLog) { Log.Debug("ViewModeSwitcher: SingleCrop(), PillarBox -> Normal"); } //Force CheckAspectRatios() update updatePending = false; isPBorLB = false; LastSwitchedAspectRatio = 0.0; frame.Dispose(); frame = null; return; } isPBorLB = false; } if (currentSettings.verboseLog) { Log.Debug("ViewModeSwitcher: SingleCrop(), Real cropH: {0}, cropV: {1}", cropH, cropV); } if ((Math.Abs(cropH - fCropH) > 5) || (Math.Abs(cropV - fCropV) > 3)) { fCropH = cropH; fCropV = cropV; updateCrop = true; } if (updateCrop) { updatePending = true; } frame.Dispose(); frame = null; }
/// <summary> /// Get the last frame of video from the Video Mixing Renderer /// (only works for WMR9 Renderless) /// </summary> /// <returns></returns> private Bitmap GetFrame() { //Log.Debug("GetFrame"); return(grabber.GetCurrentImage()); }
/// <summary> /// Finds the bounds and crops accordingly immediately /// </summary> private void SingleCrop() { if (GUIGraphicsContext.RenderBlackImage) { LastDetectionResult = false; return; } Bitmap frame = grabber.GetCurrentImage(); if (frame == null || frame.Height == 0 || frame.Width == 0) { LastDetectionResult = false; return; } Rectangle bounds = new Rectangle(); if (!analyzer.FindBounds(frame, ref bounds)) { LastDetectionResult = false; return; } int cropH = Math.Min(GUIGraphicsContext.VideoSize.Width - bounds.Right, bounds.Left); if (cropH < overScan) { cropH = overScan; } int cropV = Math.Min(GUIGraphicsContext.VideoSize.Height - bounds.Bottom, bounds.Top); if (cropV < overScan / LastSwitchedAspectRatio) { cropV = (int)(overScan / LastSwitchedAspectRatio); } if (cropV > cropH / LastSwitchedAspectRatio) { cropV = (int)(cropH / LastSwitchedAspectRatio); } float newasp = (float)(frame.Width - cropH * 2) / (float)(frame.Height - cropV * 2); if (newasp < 1) // faulty crop { cropH = overScan; cropV = (int)(overScan / LastSwitchedAspectRatio); } if (cropH != cropSettings.Left || cropV != cropSettings.Top) { cropSettings.Top = cropV; //bounds.Top; cropSettings.Bottom = cropV; // GUIGraphicsContext.VideoSize.Height - (bounds.Bottom + 1); cropSettings.Left = cropH; // bounds.Left; cropSettings.Right = cropH; // GUIGraphicsContext.VideoSize.Width - (bounds.Right + 1); SetCropMode(); } if (newasp >= 1) { float asp = (float)(frame.Width) / (float)(frame.Height); //Log.Debug("asp: {0}, newasp: {1}", asp, newasp); if (Math.Abs(asp - newasp) > 0.2 && LastSwitchedAspectRatio > 1.5) { SetAspectRatio("4:3 inside 16:9", Geometry.Type.NonLinearStretch); } } frame.Dispose(); frame = null; }