//双边滤波 void OnBilateralFilter(bool value) { m_blurImage.enabled = true; if (!value) { return; } Mat dstMat = new Mat(); dstMat = srcMat.clone(); int d = 25; double sigmaColor = 50d; double sigmaSpace = 35d; Imgproc.bilateralFilter(srcMat, dstMat, d, sigmaColor, sigmaSpace); Texture2D t2d = new Texture2D(dstMat.width(), dstMat.height()); Sprite sp = Sprite.Create(t2d, new UnityEngine.Rect(0, 0, t2d.width, t2d.height), Vector2.zero); m_blurImage.sprite = sp; m_blurImage.preserveAspect = true; Utils.matToTexture2D(dstMat, t2d); }
// Create a grayscale face image that has a standard size and contrast & brightness. // "srcImg" should be a copy of the whole color camera frame, so that it can draw the eye positions onto. // If 'doLeftAndRightSeparately' is true, it will process left & right sides seperately, // so that if there is a strong light on one side but not the other, it will still look OK. // Performs Face Preprocessing as a combination of: // - geometrical scaling, rotation and translation using Eye Detection, // - smoothing away image noise using a Bilateral Filter, // - standardize the brightness on both left and right sides of the face independently using separated Histogram Equalization, // - removal of background and hair using an Elliptical Mask. // Returns either a preprocessed face square image or NULL (ie: couldn't detect the face and 2 eyes). // If a face is found, it can store the rect coordinates into 'storeFaceRect' and 'storeLeftEye' & 'storeRightEye' if given, // and eye search regions into 'searchedLeftEye' & 'searchedRightEye' if given. public static Mat GetPreprocessedFace(Mat srcImg, int desiredFaceWidth, CascadeClassifier faceCascade, CascadeClassifier eyeCascade1, CascadeClassifier eyeCascade2, bool doLeftAndRightSeparately, ref Rect storeFaceRect, ref Point storeLeftEye, ref Point storeRightEye, ref Rect searchedLeftEye, ref Rect searchedRightEye) { // Use square faces. int desiredFaceHeight = desiredFaceWidth; // Mark the detected face region and eye search regions as invalid, in case they aren't detected. if (storeFaceRect != null) { storeFaceRect.width = -1; } if (storeLeftEye != null) { storeLeftEye.x = -1; } if (storeRightEye != null) { storeRightEye.x = -1; } if (searchedLeftEye != null) { searchedLeftEye.width = -1; } if (searchedRightEye != null) { searchedRightEye.width = -1; } // Find the largest face. Rect faceRect; DetectObject.DetectLargestObject(srcImg, faceCascade, out faceRect); // Check if a face was detected. if (faceRect.width > 0) { // Give the face rect to the caller if desired. if (storeFaceRect != null) { storeFaceRect = faceRect; } // Get the detected face image. using (Mat faceImg = new Mat(srcImg, faceRect)) { // If the input image is not grayscale, then convert the RGB or RGBA color image to grayscale. using (Mat gray = new Mat()) { if (faceImg.channels() == 3) { Imgproc.cvtColor(faceImg, gray, Imgproc.COLOR_RGB2GRAY); } else if (faceImg.channels() == 4) { Imgproc.cvtColor(faceImg, gray, Imgproc.COLOR_RGBA2GRAY); } else { // Access the input image directly, since it is already grayscale. faceImg.copyTo(gray); } // Search for the 2 eyes at the full resolution, since eye detection needs max resolution possible! Point leftEye, rightEye; detectBothEyes(gray, eyeCascade1, eyeCascade2, out leftEye, out rightEye, ref searchedLeftEye, ref searchedRightEye); // Give the eye results to the caller if desired. if (storeLeftEye != null) { storeLeftEye = leftEye; } if (storeRightEye != null) { storeRightEye = rightEye; } // Check if both eyes were detected. if (leftEye.x >= 0 && rightEye.x >= 0) { // Make the face image the same size as the training images. // Since we found both eyes, lets rotate & scale & translate the face so that the 2 eyes // line up perfectly with ideal eye positions. This makes sure that eyes will be horizontal, // and not too far left or right of the face, etc. // Get the center between the 2 eyes. Point eyesCenter = new Point((leftEye.x + rightEye.x) * 0.5f, (leftEye.y + rightEye.y) * 0.5f); // Get the angle between the 2 eyes. double dy = (rightEye.y - leftEye.y); double dx = (rightEye.x - leftEye.x); double len = Math.Sqrt(dx * dx + dy * dy); double angle = Math.Atan2(dy, dx) * 180.0d / Math.PI; // Convert from radians to degrees. // Hand measurements shown that the left eye center should ideally be at roughly (0.19, 0.14) of a scaled face image. const double DESIRED_RIGHT_EYE_X = (1.0d - DESIRED_LEFT_EYE_X); // Get the amount we need to scale the image to be the desired fixed size we want. double desiredLen = (DESIRED_RIGHT_EYE_X - DESIRED_LEFT_EYE_X) * desiredFaceWidth; double scale = desiredLen / len; // Get the transformation matrix for rotating and scaling the face to the desired angle & size. Mat rot_mat = Imgproc.getRotationMatrix2D(eyesCenter, angle, scale); // Shift the center of the eyes to be the desired center between the eyes. double[] shiftX = rot_mat.get(0, 2); shiftX [0] += desiredFaceWidth * 0.5f - eyesCenter.x; rot_mat.put(0, 2, shiftX); double[] shiftY = rot_mat.get(1, 2); shiftY [0] += desiredFaceHeight * DESIRED_LEFT_EYE_Y - eyesCenter.y; rot_mat.put(1, 2, shiftY); // Rotate and scale and translate the image to the desired angle & size & position! // Note that we use 'w' for the height instead of 'h', because the input face has 1:1 aspect ratio. using (Mat warped = new Mat(desiredFaceHeight, desiredFaceWidth, CvType.CV_8UC1, GRAY)) // Clear the output image to a default grey. using (Mat filtered = new Mat(desiredFaceHeight, desiredFaceWidth, CvType.CV_8UC1)) using (Mat mask = new Mat(desiredFaceHeight, desiredFaceWidth, CvType.CV_8UC1, BLACK)) { // Start with an empty mask. Imgproc.warpAffine(gray, warped, rot_mat, warped.size()); //imshow("warped", warped); // Give the image a standard brightness and contrast, in case it was too dark or had low contrast. if (!doLeftAndRightSeparately) { // Do it on the whole face. Imgproc.equalizeHist(warped, warped); } else { // Do it seperately for the left and right sides of the face. equalizeLeftAndRightHalves(warped); } //imshow("equalized", warped); // Use the "Bilateral Filter" to reduce pixel noise by smoothing the image, but keeping the sharp edges in the face. Imgproc.bilateralFilter(warped, filtered, 0, 20.0, 2.0); //imshow("filtered", filtered); // Filter out the corners of the face, since we mainly just care about the middle parts. // Draw a filled ellipse in the middle of the face-sized image. Point faceCenter = new Point(desiredFaceWidth / 2, Math.Round(desiredFaceHeight * FACE_ELLIPSE_CY)); Size size = new Size(Math.Round(desiredFaceWidth * FACE_ELLIPSE_W), Math.Round(desiredFaceHeight * FACE_ELLIPSE_H)); Imgproc.ellipse(mask, faceCenter, size, 0, 0, 360, WHITE, Core.FILLED); //imshow("mask", mask); // Use the mask, to remove outside pixels. Mat dstImg = new Mat(desiredFaceHeight, desiredFaceWidth, CvType.CV_8UC1, GRAY); // Clear the output image to a default gray. /* * namedWindow("filtered"); * imshow("filtered", filtered); * namedWindow("dstImg"); * imshow("dstImg", dstImg); * namedWindow("mask"); * imshow("mask", mask); */ // Apply the elliptical mask on the face. filtered.copyTo(dstImg, mask); // Copies non-masked pixels from filtered to dstImg. //imshow("dstImg", dstImg); return(dstImg); } } } } /* * else { * // Since no eyes were found, just do a generic image resize. * resize(gray, tmpImg, Size(w,h)); * } */ } return(null); }