/// <summary> /// Get the intrinsic calibration parameters for a given camera, this also aligns the camera intrinsics based /// on device orientation. /// /// For example, if the device orientation is portrait and camera intrinsics is in /// landscape. This function will inverse the intrinsic x and y, and report intrinsics in portrait mode. /// /// The intrinsics are as specified by the TangoCameraIntrinsics struct. Intrinsics are read from the /// on-device intrinsics file (typically <code>/sdcard/config/calibration.xml</code>, but to ensure /// compatibility applications should only access these parameters via the API), or default internal model /// parameters corresponding to the device are used if the calibration.xml file is not found. /// </summary> /// <param name="cameraId">The camera ID to retrieve the calibration intrinsics for.</param> /// <param name="alignedIntrinsics"> /// A TangoCameraIntrinsics filled with calibration intrinsics for the camera, this intrinsics is also /// aligned with device orientation. /// </param> public static void GetDeviceOientationAlignedIntrinsics(TangoEnums.TangoCameraId cameraId, TangoCameraIntrinsics alignedIntrinsics) { TangoCameraIntrinsics intrinsics = new TangoCameraIntrinsics(); GetIntrinsics(TangoEnums.TangoCameraId.TANGO_CAMERA_COLOR, intrinsics); float intrinsicsRatio = (float)intrinsics.width / (float)intrinsics.height; bool isLandscape = (AndroidHelper.GetDefaultOrientation() + (int)AndroidHelper.GetDisplayRotation()) % 2 == 0; // If the intrinsics ratio and camera render ratio don't agree with each other, we invert the intrinsics // reading to align to camera render orientation. if ((!isLandscape && intrinsicsRatio > 1.0f) || (isLandscape && intrinsicsRatio < 1.0f)) { alignedIntrinsics.cx = intrinsics.cy; alignedIntrinsics.cy = intrinsics.cx; alignedIntrinsics.fx = intrinsics.fy; alignedIntrinsics.fy = intrinsics.fx; alignedIntrinsics.height = intrinsics.width; alignedIntrinsics.width = intrinsics.height; } else { alignedIntrinsics.cx = intrinsics.cx; alignedIntrinsics.cy = intrinsics.cy; alignedIntrinsics.fx = intrinsics.fx; alignedIntrinsics.fy = intrinsics.fy; alignedIntrinsics.height = intrinsics.height; alignedIntrinsics.width = intrinsics.width; } alignedIntrinsics.distortion0 = intrinsics.distortion0; alignedIntrinsics.distortion1 = intrinsics.distortion1; alignedIntrinsics.distortion2 = intrinsics.distortion2; alignedIntrinsics.distortion3 = intrinsics.distortion3; alignedIntrinsics.distortion4 = intrinsics.distortion4; alignedIntrinsics.camera_id = intrinsics.camera_id; alignedIntrinsics.calibration_type = intrinsics.calibration_type; }
/// <summary> /// Update AR screen rendering and attached Camera's projection matrix. /// </summary> /// <param name="displayRotation">Activity (screen) rotation.</param> /// <param name="colorCameraRotation">Color camera sensor rotation.</param> private void _SetRenderAndCamera(OrientationManager.Rotation displayRotation, OrientationManager.Rotation colorCameraRotation) { float cameraWidth = (float)Screen.width; float cameraHeight = (float)Screen.height; #pragma warning disable 0219 // Here we are computing if current display orientation is landscape or portrait. // AndroidHelper.GetAndroidDefaultOrientation() returns 1 if device default orientation is in portrait, // returns 2 if device default orientation is landscape. Adding device default orientation with // how much the display is rotated from default orientation will get us the result of current display // orientation. (landscape vs. portrait) bool isLandscape = (AndroidHelper.GetDefaultOrientation() + (int)displayRotation) % 2 == 0; bool needToFlipCameraRatio = false; float cameraRatio = (float)Screen.width / (float)Screen.height; #pragma warning restore 0219 #if !UNITY_EDITOR // In most cases, we don't need to flip the camera width and height. However, in some cases Unity camera // only updates a couple of frames after the display changed callback from Android; thus, we need to flip the width // and height in this case. // // This does not happen in the editor, because the emulated device does not ever rotate. needToFlipCameraRatio = (!isLandscape & (cameraRatio > 1.0f)) || (isLandscape & (cameraRatio < 1.0f)); if (needToFlipCameraRatio) { cameraRatio = 1.0f / cameraRatio; float tmp = cameraWidth; cameraWidth = cameraHeight; cameraHeight = tmp; } #endif TangoCameraIntrinsics alignedIntrinsics = new TangoCameraIntrinsics(); TangoCameraIntrinsics intrinsics = new TangoCameraIntrinsics(); VideoOverlayProvider.GetDeviceOrientationAlignedIntrinsics(TangoEnums.TangoCameraId.TANGO_CAMERA_COLOR, alignedIntrinsics); VideoOverlayProvider.GetIntrinsics(TangoEnums.TangoCameraId.TANGO_CAMERA_COLOR, intrinsics); if (alignedIntrinsics.width != 0 && alignedIntrinsics.height != 0) { // The camera to which this script is attached is an Augmented Reality camera. The color camera // image must fill that camera's viewport. That means we must clip the color camera image to make // its ratio the same as the Unity camera. If we don't do this the color camera image will be // stretched non-uniformly, making a circle into an ellipse. float widthRatio = (float)cameraWidth / (float)alignedIntrinsics.width; float heightRatio = (float)cameraHeight / (float)alignedIntrinsics.height; if (widthRatio >= heightRatio) { m_uOffset = 0; m_vOffset = (1 - (heightRatio / widthRatio)) / 2; } else { m_uOffset = (1 - (widthRatio / heightRatio)) / 2; m_vOffset = 0; } // Note that here we are passing in non-inverted intrinsics, because the YUV conversion is still operating // on native buffer layout. OrientationManager.Rotation rotation = TangoSupport.RotateFromAToB(displayRotation, colorCameraRotation); _MaterialUpdateForIntrinsics(m_uOffset, m_vOffset, rotation); _CameraUpdateForIntrinsics(m_camera, alignedIntrinsics, m_uOffset, m_vOffset); if (m_arCameraPostProcess != null) { m_arCameraPostProcess.SetupIntrinsic(intrinsics); } } else { Debug.LogError("AR Camera intrinsic is not valid."); } }