{
  "source_file": "0930",
  "source_platform": "技术平台",
  "author": "Adrian Rosebrock / PyImageSearch",
  "title_listed": "Face Alignment with OpenCV and Python",
  "keywords": "人脸对齐,OpenCV,关键点",
  "domain": "图像处理",
  "content_type": "教程",
  "is_paid_flag": "否",
  "url": "https://pyimagesearch.com/2017/05/22/face-alignment-with-opencv-and-python/",
  "publish_date": "2017-05",
  "corpus_stage": "预训练/SFT",
  "notes": "OpenCV人脸对齐",
  "id": "4ae466d6e193",
  "collected_at": "2026-10-02T17:24:33",
  "title": "Face Alignment with OpenCV and Python - PyImageSearch",
  "body_markdown": "Click here to download the source code to this post\nContinuing our series of blog posts on\nfacial landmarks\n, today we are going to discuss\nface alignment\n, the process of:\nIdentifying the geometric structure of faces in digital images.\nAttempting to obtain a canonical alignment of the face based on translation, scale, and rotation.\nThere are many forms of face alignment.\nSome methods try to impose a (pre-defined) 3D model and then apply a transform to the input image such that the landmarks on the input face match the landmarks on the 3D model.\nOther, more simplistic methods (like the one discussed in this blog post), rely\nonly on the facial landmarks themselves\n(in particular, the eye regions) to obtain a\nnormalized rotation, translation, and scale representation of the face\n.\nThe reason we perform this normalization is due to the fact that many facial recognition algorithms, including\nEigenfaces\n,\nLBPs for face recognition\n,\nFisherfaces\n, and\ndeep learning/metric methods\ncan all benefit from applying facial alignment before trying to identify the face.\nThus, face alignment can be seen as a form of “data normalization”. Just as you may normalize a set of feature vectors via zero centering or scaling to unit norm prior to training a machine learning model, it’s very common to align the faces in your dataset before training a face recognizer.\nBy performing this process, you’ll enjoy higher accuracy from your face recognition models.\nNote:\nIf you’re interested in learning more about creating your own custom face recognizers, be sure to refer to the\nPyImageSearch Gurus course\nwhere I provide\ndetailed tutorials\non face recognition.\nTo learn more about face alignment and normalization,\njust keep reading\n.\nLooking for the source code to this post?\nJump Right To The Downloads Section\nFace alignment with OpenCV and Python\nThe purpose of this blog post is to demonstrate how to align a face using OpenCV, Python, and\nfacial landmarks\n.\nGiven a set of facial landmarks (the input coordinates) our goal is to\nwarp\nand\ntransform\nthe image to an output coordinate space.\nIn this output coordinate space, all faces across an entire dataset should:\nBe centered in the image.\nBe rotated that such the eyes lie on a horizontal line (i.e., the face is rotated such that the eyes lie along the same\ny\n-coordinates).\nBe scaled such that the size of the faces are approximately identical.\nTo accomplish this, we’ll first implement a dedicated Python class to align faces using an affine transformation. I’ve already implemented this\nFaceAligner class in imutils\n.\nNote:\nAffine transformations are used for rotating, scaling, translating, etc. We can pack all three of the above requirements into a single\ncv2.warpAffine\ncall; the trick is creating the rotation matrix,\nM\n.\nWe’ll then create an example driver Python script to accept an input image, detect faces, and align them.\nFinally, we’ll review the results from our face alignment with OpenCV process.\nImplementing our face aligner\nThe face alignment algorithm itself is based on Chapter 8 of\nMastering OpenCV with Practical Computer Vision Projects\n(Baggio, 2012), which I highly recommend if you have a C++ background or interest. The book provides open-access code samples\non GitHub\n.\nLet’s get started by examining our\nFaceAligner\nimplementation and understanding what’s going on under the hood.\n# import the necessary packages\nfrom .helpers import FACIAL_LANDMARKS_IDXS\nfrom .helpers import shape_to_np\nimport numpy as np\nimport cv2\n\nclass FaceAligner:\n    def __init__(self, predictor, desiredLeftEye=(0.35, 0.35),\n        desiredFaceWidth=256, desiredFaceHeight=None):\n        # store the facial landmark predictor, desired output left\n        # eye position, and desired output face width + height\n        self.predictor = predictor\n        self.desiredLeftEye = desiredLeftEye\n        self.desiredFaceWidth = desiredFaceWidth\n        self.desiredFaceHeight = desiredFaceHeight\n\n        # if the desired face height is None, set it to be the\n        # desired face width (normal behavior)\n        if self.desiredFaceHeight is None:\n            self.desiredFaceHeight = self.desiredFaceWidth\nLines 2-5\nhandle our imports. To read about facial landmarks and our associated helper functions, be sure to\ncheck out this previous post\n.\nOn\nLine 7\n, we begin our\nFaceAligner\nclass with our constructor being defined on\nLines 8-20\n.\nOur constructor has 4 parameters:\npredictor\n: The facial landmark predictor model.\ndesiredLeftEye\n: An optional\n(x, y)\ntuple with the default shown, specifying the desired output left eye position. For this variable, it is common to see percentages within the range of 20-40%. These percentages control how much of the face is visible after alignment. The exact percentages used will vary on an application-to-application basis. With 20% you’ll basically be getting a “zoomed in” view of the face, whereas with larger values the face will appear more “zoomed out.”\ndesiredFaceWidth\n: Another optional parameter that defines our desired face with in pixels. We default this value to 256 pixels.\ndesiredFaceHeight\n: The final optional parameter specifying our desired face height value in pixels.\nEach of these parameters is set to a corresponding instance variable on\nLines 12-15\n.\nNext, let’s decide whether we want a square image of a face, or something rectangular.\nLines 19 and 20\ncheck if the\ndesiredFaceHeight\nis\nNone\n, and if so, we set it to the\ndesiredFaceWidth\n, meaning that the face is square. A square image is the typical case. Alternatively, we can specify different values for both\ndesiredFaceWidth\nand\ndesiredFaceHeight\nto obtain a rectangular region of interest.\nNow that we have constructed our\nFaceAligner\nobject, we will next define a function which aligns the face.\nThis function is a bit long, so I’ve broken it up into 5 code blocks to make it more digestible:\ndef align(self, image, gray, rect):\n        # convert the landmark (x, y)-coordinates to a NumPy array\n        shape = self.predictor(gray, rect)\n        shape = shape_to_np(shape)\n\n        # extract the left and right eye (x, y)-coordinates\n        (lStart, lEnd) = FACIAL_LANDMARKS_IDXS[\"left_eye\"]\n        (rStart, rEnd) = FACIAL_LANDMARKS_IDXS[\"right_eye\"]\n        leftEyePts = shape[lStart:lEnd]\n        rightEyePts = shape[rStart:rEnd]\nBeginning on\nLine 22\n, we define the align function which accepts three parameters:\nimage\n: The RGB input image.\ngray\n: The grayscale input image.\nrect\n: The bounding box rectangle produced by dlib’s HOG face detector.\nOn\nLines 24 and 25\n, we apply dlib’s facial landmark predictor and convert the landmarks into\n(x, y)\n-coordinates in NumPy format.\nNext, on\nLines 28 and 29\nwe read the\nleft_eye\nand\nright_eye\nregions from the\nFACIAL_LANDMARK_IDXS\ndictionary, found in the\nhelpers.py\nscript. These 2-tuple values are stored in left/right eye starting and ending indices.\nThe\nleftEyePts\nand\nrightEyePts\nare extracted from the shape list using the starting and ending indices on\nLines 30 and 31\n.\nNext, let’s will compute the center of each eye as well as the angle between the eye centroids.\nThis angle serves as the key component for aligning our image.\nThe angle of the green line between the eyes, shown in\nFigure 1\nbelow, is the one that we are concerned about.\nFigure 1:\nComputing the angle between two eyes for face alignment.\nTo see how the angle is computed, refer to the code block below:\n# compute the center of mass for each eye\n        leftEyeCenter = leftEyePts.mean(axis=0).astype(\"int\")\n        rightEyeCenter = rightEyePts.mean(axis=0).astype(\"int\")\n\n        # compute the angle between the eye centroids\n        dY = rightEyeCenter[1] - leftEyeCenter[1]\n        dX = rightEyeCenter[0] - leftEyeCenter[0]\n        angle = np.degrees(np.arctan2(dY, dX)) - 180\nOn\nLines 34 and 35\nwe compute the centroid, also known as the center of mass, of each eye by averaging all\n(x, y)\npoints of each eye, respectively.\nGiven the eye centers, we can compute differences in\n(x, y)\n-coordinates and take the arc-tangent to obtain angle of rotation between eyes.\nThis angle will allow us to correct for rotation.\nTo determine the angle, we start by computing the delta in the\ny\n-direction,\ndY\n. This is done by finding the difference between the\nrightEyeCenter\nand the\nleftEyeCenter\non\nLine 38\n.\nSimilarly, we compute\ndX\n, the delta in the\nx\n-direction on\nLine 39\n.\nNext, on\nLine 40\n, we compute the angle of the face rotation. We use NumPy’s\narctan2\nfunction with arguments\ndY\nand\ndX\n, followed by converting to degrees while subtracting 180 to obtain the angle.\nIn the following code block we compute the desired right eye coordinate (as a function of the left eye placement) as well as calculating the scale of the new resulting image.\n# compute the desired right eye x-coordinate based on the\n        # desired x-coordinate of the left eye\n        desiredRightEyeX = 1.0 - self.desiredLeftEye[0]\n\n        # determine the scale of the new resulting image by taking\n        # the ratio of the distance between eyes in the *current*\n        # image to the ratio of distance between eyes in the\n        # *desired* image\n        dist = np.sqrt((dX ** 2) + (dY ** 2))\n        desiredDist = (desiredRightEyeX - self.desiredLeftEye[0])\n        desiredDist *= self.desiredFaceWidth\n        scale = desiredDist / dist\nOn\nLine 44\n, we calculate the desired right eye based upon the desired left eye\nx\n-coordinate. We subtract\nself.desiredLeftEye[0]\nfrom\n1.0\nbecause the\ndesiredRightEyeX\nvalue should be equidistant from the right edge of the image as the corresponding left eye\nx\n-coordinate is from its left edge.\nWe can then determine the\nscale\nof the face by taking the ratio of the distance between the eyes in the current image to the distance between eyes in the desired image\nFirst, we compute the Euclidean distance ratio,\ndist\n, on\nLine 50\n.\nNext, on\nLine 51\n, using the difference between the right and left eye\nx\n-values we compute the desired distance,\ndesiredDist\n.\nWe update the\ndesiredDist\nby multiplying it by the\ndesiredFaceWidth\non\nLine 52\n. This essentially scales our eye distance based on the desired width.\nFinally, our scale is computed by dividing\ndesiredDist\nby our previously calculated\ndist\n.\nNow that we have our rotation\nangle\nand\nscale\n, we will need to take a few steps before we compute the affine transformation. This includes finding the midpoint between the eyes as well as calculating the rotation matrix and updating its translation component:\n# compute center (x, y)-coordinates (i.e., the median point)\n        # between the two eyes in the input image\n        eyesCenter = ((leftEyeCenter[0] + rightEyeCenter[0]) // 2,\n            (leftEyeCenter[1] + rightEyeCenter[1]) // 2)\n\n        # grab the rotation matrix for rotating and scaling the face\n        M = cv2.getRotationMatrix2D(eyesCenter, angle, scale)\n\n        # update the translation component of the matrix\n        tX = self.desiredFaceWidth * 0.5\n        tY = self.desiredFaceHeight * self.desiredLeftEye[1]\n        M[0, 2] += (tX - eyesCenter[0])\n        M[1, 2] += (tY - eyesCenter[1])\nOn\nLines 57 and 58\n, we compute\neyesCenter\n, the midpoint between the left and right eyes. This will be used in our rotation matrix calculation. In essence, this midpoint is at the top of the nose and is the point at which we will rotate the face around:\nFigure 2:\nComputing the midpoint (blue) between two eyes. This will serve as the\n(x, y)\n-coordinate in which we rotate the face around.\nTo compute our rotation matrix,\nM\n, we utilize\ncv2.getRotationMatrix2D\nspecifying\neyesCenter\n,\nangle\n, and\nscale\n(\nLine 61\n). Each of these three values have been previously computed, so refer back to\nLine 40\n,\nLine 53\n, and\nLine 57\nas needed.\nA description of the parameters to\ncv2.getRotationMatrix2D\nfollow:\neyesCenter\n: The midpoint between the eyes is the point at which we will rotate the face around.\nangle\n: The angle we will rotate the face to to ensure the eyes lie along the same horizontal line.\nscale\n: The percentage that we will scale up or down the image, ensuring that the image scales to the desired size.\nNow we must update the translation component of the matrix so that the face is still in the image after the affine transform.\nOn\nLine 64\n, we take half of the\ndesiredFaceWidth\nand store the value as\ntX\n, the translation in the\nx\n-direction.\nTo compute\ntY\n, the translation in the\ny\n-direction, we multiply the\ndesiredFaceHeight\nby the desired left eye\ny\n-value,\ndesiredLeftEye[1]\n.\nUsing\ntX\nand\ntY\n, we update the translation component of the matrix by subtracting each value from their corresponding eyes midpoint value,\neyesCenter\n(\nLines 66 and 67\n).\nWe can now apply our affine transformation to align the face:\n# apply the affine transformation\n        (w, h) = (self.desiredFaceWidth, self.desiredFaceHeight)\n        output = cv2.warpAffine(image, M, (w, h),\n            flags=cv2.INTER_CUBIC)\n\n        # return the aligned face\n        return output\nFor convenience we store the\ndesiredFaceWidth\nand\ndesiredFaceHeight\ninto\nw\nand\nh\nrespectively (\nLine 70\n).\nThen we perform our last step on\nLines 70 and 71\nby making a call to\ncv2.warpAffine\n. This function call requires 3 parameters and 1 optional parameter:\nimage\n: The face image.\nM\n: The translation, rotation, and scaling matrix.\n(w, h)\n: The desired width and height of the output face.\nflags\n: The interpolation algorithm to use for the warp, in this case\nINTER_CUBIC\n. To read about the other possible flags and image transformations, please consult\nthe OpenCV documentation\n.\nFinally, we return the aligned face on\nLine 75\n.\nAligning faces with OpenCV and Python\nNow let’s put this alignment class to work with a simple driver script. Open up a new file, name it\nalign_faces.py\n, and let’s get to coding.\n# import the necessary packages\nfrom imutils.face_utils import FaceAligner\nfrom imutils.face_utils import rect_to_bb\nimport argparse\nimport imutils\nimport dlib\nimport cv2\n\n# construct the argument parser and parse the arguments\nap = argparse.ArgumentParser()\nap.add_argument(\"-p\", \"--shape-predictor\", required=True,\nhelp=\"path to facial landmark predictor\")\nap.add_argument(\"-i\", \"--image\", required=True,\nhelp=\"path to input image\")\nargs = vars(ap.parse_args())\nOn\nLines 2-7\nwe import required packages.\nIf you do not have\nimutils\nand/or\ndlib\ninstalled on your system\n, then make sure you install/upgrade them via\npip\n:\n$ pip install --upgrade imutils\n$ pip install --upgrade dlib\nNote:\nIf you are using Python virtual environments (as all of my\nOpenCV install tutorials\ndo), make sure you use the\nworkon\ncommand to access your virtual environment first, and then install/upgrade\nimutils\nand\ndlib\n.\nUsing\nargparse\non\nLines 10-15\n, we specify 2 required command line arguments:\n--shape-predictor\n: The dlib facial landmark predictor.\n--image\n: The image containing faces.\nIn the next block of code we initialize our HOG-based detector (\nHistogram of Oriented Gradients\n), our facial landmark predictor, and our face aligner:\n# initialize dlib's face detector (HOG-based) and then create\n# the facial landmark predictor and the face aligner\ndetector = dlib.get_frontal_face_detector()\npredictor = dlib.shape_predictor(args[\"shape_predictor\"])\nfa = FaceAligner(predictor, desiredFaceWidth=256)\nLine 19\ninitializes our detector object using dlib’s\nget_frontal_face_detector\n.\nOn\nLine 20\nwe instantiate our facial landmark predictor using,\n--shape-predictor\n, the path to dlib’s pre-trained predictor.\nWe make use of the\nFaceAligner\nclass that we just built in the previous section by initializing a an object,\nfa\n, on\nLine 21\n. We specify a face width of 256 pixels.\nNext, let’s load our image and prepare it for face detection:\n# load the input image, resize it, and convert it to grayscale\nimage = cv2.imread(args[\"image\"])\nimage = imutils.resize(image, width=800)\ngray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)\n\n# show the original input image and detect faces in the grayscale\n# image\ncv2.imshow(\"Input\", image)\nrects = detector(gray, 2)\nOn\nLine 24\n, we load our image specified by the command line argument\n–-image\n. We resize the image maintaining the aspect ratio on\nLine 25\nto have a width of 800 pixels. We then convert the image to grayscale on\nLine 26\n.\nDetecting faces in the input image is handled on\nLine 31\nwhere we apply dlib’s face detector. This function returns\nrects\n, a list of bounding boxes around the faces our detector has found.\nIn the next block, we iterate through\nrects\n, align each face, and display the original and aligned images.\n# loop over the face detections\nfor rect in rects:\n\t# extract the ROI of the *original* face, then align the face\n\t# using facial landmarks\n\t(x, y, w, h) = rect_to_bb(rect)\n\tfaceOrig = imutils.resize(image[y:y + h, x:x + w], width=256)\n\tfaceAligned = fa.align(image, gray, rect)\n\n\t# display the output images\n\tcv2.imshow(\"Original\", faceOrig)\n\tcv2.imshow(\"Aligned\", faceAligned)\n\tcv2.waitKey(0)\nWe begin our loop on\nLine 34\n.\nFor each bounding box\nrect\npredicted by dlib we convert it to the format\n(x, y, w, h)\n(\nLine 37\n).\nSubsequently, we resize the box to a width of 256 pixels,\nmaintaining the aspect ratio\n, on\nLine 38\n. We store this original, but resized image, as\nfaceOrig\n.\nOn\nLine 39\n, we align the image, specifying our image, grayscale image, and rectangle.\nFinally,\nLines 42 and 43\ndisplay the original and corresponding aligned face image to the screen in respective windows.\nOn\nLine 44\n, we wait for the user to press a key with either window in focus, before displaying the next original/aligned image pair.\nThe process on\nLines 35-44\nis repeated for all faces detected, then the script exits.\nTo see our face aligner in action, head to next section.\nFace alignment results\nLet’s go ahead and apply our face aligner to some example images. Make sure you use the\n“Downloads”\nsection of this blog post to download the source code + example images.\nAfter unpacking the archive, execute the following command:\n$ python align_faces.py \\\n\t--shape-predictor shape_predictor_68_face_landmarks.dat \\\n\t--image images/example_01.jpg\nFrom there you’ll see the following input image, a photo of myself and my financée, Trisha:\nFigure 3:\nAn input image to our OpenCV face aligner.\nThis image contains two faces, therefore we’ll be performing\ntwo\nfacial alignments.\nThe first is seen below:\nFigure 4:\nAligning faces with OpenCV.\nOn the\nleft\nwe have the original detected face. The\naligned face\nis then displayed on the\nright\n.\nNow for Trisha’s face:\nFigure 5:\nFacial alignment with OpenCV and Python.\nNotice how after facial alignment\nboth\nof our faces are the same scale\nand\nthe eyes appear in the same output\n(x, y)\n-coordinates.\nLet’s try a second example:\n$ python align_faces.py \\\n\t--shape-predictor shape_predictor_68_face_landmarks.dat \\\n\t--image images/example_02.jpg\nHere I am enjoying a glass of wine on Thanksgiving morning:\nFigure 6:\nAn input image to our face aligner.\nAfter detecting my face, it is then aligned as the following figure demonstrates:\nFigure 7:\nUsing facial landmarks to align faces in images.\nHere is a third example, this one of myself and my father last spring after cooking up a batch of soft shell crabs:\n$ python align_faces.py \\\n\t--shape-predictor shape_predictor_68_face_landmarks.dat \\\n\t--image images/example_03.jpg\nFigure 8:\nAnother example input to our face aligner.\nMy father’s face is first aligned:\nFigure 9:\nApplying facial alignment using OpenCV and Python.\nFollowed by my own:\nFigure 10:\nUsing face alignment to obtain canonical representations of faces.\nThe fourth example is a photo of my grandparents the last time they visited North Carolina:\n$ python align_faces.py \\\n\t--shape-predictor shape_predictor_68_face_landmarks.dat \\\n\t--image images/example_04.jpg\nFigure 11:\nInputting an image to our face alignment algorithm.\nMy grandmother’s face is aligned first:\nFigure 12:\nPerforming face alignment using computer vision.\nAnd then my grandfather’s:\nFigure 13:\nFace alignment in unaffected by the person in the photo wearing glasses.\nDespite both of them wearing glasses the faces are correctly aligned.\nLet’s do one final example:\n$ python align_faces.py \\\n\t--shape-predictor shape_predictor_68_face_landmarks.dat \\\n\t--image images/example_05.jpg\nFigure 14:\nThe final example input image to our face aligner.\nAfter applying face detection, Trisha’s face is aligned first:\nFigure 15:\nFacial alignment using facial landmarks.\nAnd then my own:\nFigure 16:\nFace alignment still works even if the input face is rotated.\nThe rotation angle of my face is detected and corrected, followed by being scaled to the appropriate size.\nTo demonstrate that this face alignment method does indeed (1) center the face, (2) rotate the face such that the eyes lie along a horizontal line, and (3) scale the faces such that they are approximately identical in size, I’ve put together a GIF animation that you can see below:\nFigure 17:\nAn animation demonstrating face alignment across multiple images.\nAs you can see, the eye locations and face sizes are near identical for every input image.\nWhat's next? We recommend\nPyImageSearch University\n.\nCourse information:\n120+ total classes • 115+ hours of on-demand code walkthrough videos • Last updated: October 2026\n★★★★★\n4.84 (128 Ratings) • 16,000+ Students Enrolled\nI strongly believe that if you had the right teacher you could\nmaster\ncomputer vision and deep learning.\nDo you think learning computer vision and deep learning has to be time-consuming, overwhelming, and complicated? Or has to involve complex mathematics and equations? Or requires a degree in computer science?\nThat’s\nnot\nthe case.\nAll you need to master computer vision and deep learning is for someone to explain things to you in\nsimple, intuitive\nterms.\nAnd that’s exactly what I do\n. My mission is to change education and how complex Artificial Intelligence topics are taught.\nIf you're serious about learning computer vision, your next stop should be PyImageSearch University, the most comprehensive computer vision, deep learning, and OpenCV course online today. Here you’ll learn how to\nsuccessfully\nand\nconfidently\napply computer vision to your work, research, and projects. Join me in computer vision mastery.\nInside PyImageSearch University you'll find:\n✓\n120+ courses\non essential computer vision, deep learning, and OpenCV topics\n✓\n94+ Certificates\nof Completion\n✓\n115+ hours\nof on-demand video\n✓\nBrand new courses released\nregularly\n, ensuring you can keep up with state-of-the-art techniques\n✓\nPre-configured Jupyter Notebooks in Google Colab\n✓ Run all code examples in your web browser — works on Windows, macOS, and Linux (no dev environment configuration required!)\n✓ Access to\ncentralized code repos for\nall\n540+ tutorials\non PyImageSearch\n✓\nEasy one-click downloads\nfor code, datasets, pre-trained models, etc.\n✓\nAccess\non mobile, laptop, desktop, etc.\nClick here to join PyImageSearch University\nSummary\nIn today’s post, we learned how to apply facial alignment with OpenCV and Python. Facial alignment is a normalization technique, often used to improve the accuracy of face recognition algorithms, including deep learning models.\nThe goal of facial alignment is to transform an input coordinate space to output coordinate space, such that all faces across an entire dataset should:\nBe centered in the image.\nBe rotated that such the eyes lie on a horizontal line (i.e., the face is rotated such that the eyes lie along the same\ny\n-coordinates).\nBe scaled such that the size of the faces are approximately identical.\nAll three goals can be accomplished using an affine transformation. The trick is determining the components of the transformation matrix,\nM\n.\nOur facial alignment algorithm hinges on knowing the\n(x, y)\n-coordinates of the eyes. In this blog post we used dlib, but you can use other facial landmark libraries as well — the same techniques apply.\nFacial landmarks tend to work better than Haar cascades or HOG detectors for facial alignment since we obtain a\nmore precise estimation\nto eye location (rather than just a bounding box).\nIf you’re interested in learning more about face recognition and object detection, be sure to take a look at the\nPyImageSearch Gurus course\nwhere I have over\n25+ lessons\non these topics.\nDownload the Source Code and FREE 17-page Resource Guide\nEnter your email address below to get a .zip of the code and a\nFREE 17-page Resource Guide on Computer Vision, OpenCV, and Deep Learning.\nInside you'll find my hand-picked tutorials, books, courses, and libraries to help you master CV and DL!\nDownload the code!\nWebsite",
  "metadata": {
    "title": "Face Alignment with OpenCV and Python - PyImageSearch",
    "description": "In this tutorial you'll learn how to perform facial alignment using OpenCV, Python, and computer vision techniques.",
    "keywords": null,
    "author": "Adrian Rosebrock",
    "og:locale": "en_US",
    "og:type": "article",
    "og:title": "Face Alignment with OpenCV and Python - PyImageSearch",
    "og:description": "In this tutorial you'll learn how to perform facial alignment using OpenCV, Python, and computer vision techniques.",
    "og:url": "https://pyimagesearch.com/2017/05/22/face-alignment-with-opencv-and-python/",
    "og:site_name": "PyImageSearch",
    "og:image": "https://pyimagesearch.com/wp-content/uploads/2017/05/face_alignment_example_01_result_01.jpg",
    "og:image:width": "600",
    "og:image:height": "341",
    "og:image:type": "image/jpeg",
    "twitter:label1": "Written by",
    "twitter:data1": "Adrian Rosebrock",
    "twitter:label2": "Est. reading time",
    "twitter:data2": "20 minutes",
    "article:published_time": "2017-05-22T14:00:59+00:00",
    "article:modified_time": "2021-04-17T18:42:17+00:00"
  },
  "paywall": {
    "listed_paid": false,
    "detected": false,
    "marker": "",
    "public_only": false,
    "policy": "付费/会员内容只保留页面公开可见部分，不绕过付费墙"
  },
  "crawl": {
    "success": true,
    "status_code": 200,
    "error": ""
  },
  "code_blocks": [
    {
      "file": "attachments/4ae466d6e193/code/body-000.txt",
      "chars": 799,
      "language": "text"
    },
    {
      "file": "attachments/4ae466d6e193/code/body-001.txt",
      "chars": 446,
      "language": "text"
    },
    {
      "file": "attachments/4ae466d6e193/code/body-002.txt",
      "chars": 384,
      "language": "text"
    },
    {
      "file": "attachments/4ae466d6e193/code/body-003.txt",
      "chars": 582,
      "language": "text"
    },
    {
      "file": "attachments/4ae466d6e193/code/body-004.txt",
      "chars": 615,
      "language": "text"
    },
    {
      "file": "attachments/4ae466d6e193/code/body-005.txt",
      "chars": 249,
      "language": "text"
    },
    {
      "file": "attachments/4ae466d6e193/code/body-006.txt",
      "chars": 464,
      "language": "text"
    },
    {
      "file": "attachments/4ae466d6e193/code/body-007.txt",
      "chars": 61,
      "language": "text"
    },
    {
      "file": "attachments/4ae466d6e193/code/body-008.txt",
      "chars": 267,
      "language": "text"
    },
    {
      "file": "attachments/4ae466d6e193/code/body-009.txt",
      "chars": 313,
      "language": "text"
    },
    {
      "file": "attachments/4ae466d6e193/code/body-010.txt",
      "chars": 395,
      "language": "text"
    },
    {
      "file": "attachments/4ae466d6e193/code/body-011.txt",
      "chars": 116,
      "language": "text"
    },
    {
      "file": "attachments/4ae466d6e193/code/body-012.txt",
      "chars": 116,
      "language": "text"
    },
    {
      "file": "attachments/4ae466d6e193/code/body-013.txt",
      "chars": 116,
      "language": "text"
    },
    {
      "file": "attachments/4ae466d6e193/code/body-014.txt",
      "chars": 116,
      "language": "text"
    },
    {
      "file": "attachments/4ae466d6e193/code/body-015.txt",
      "chars": 116,
      "language": "text"
    }
  ],
  "images": [
    {
      "status": "ok",
      "url": "https://b2633864.assetcdn.net/2633864/wp-content/uploads/2017/05/face_alignment_animation.gif?lossy=2&strip=1&webp=1",
      "bytes": 184515,
      "reused": true,
      "local_path": "attachments/4ae466d6e193/images/033.gif",
      "src": "https://b2633864.assetcdn.net/2633864/wp-content/uploads/2017/05/face_alignment_animation.gif?lossy=2&strip=1&webp=1",
      "alt": "",
      "downloaded": true
    },
    {
      "status": "ok",
      "url": "https://b2633864.assetcdn.net/2633864/wp-content/uploads/2020/01/source-code-icon.png?lossy=2&strip=1&webp=1",
      "bytes": 884,
      "reused": false,
      "local_path": "attachments/4ae466d6e193/images/body-0f8ec41d2103feb4399d.png",
      "src": "https://b2633864.assetcdn.net/2633864/wp-content/uploads/2020/01/source-code-icon.png?lossy=2&strip=1&webp=1",
      "alt": "",
      "downloaded": true
    },
    {
      "status": "ok",
      "url": "https://b2633864.assetcdn.net/2633864/wp-content/uploads/2017/05/face_alignment_angle.jpg?lossy=2&strip=1&webp=1",
      "bytes": 19823,
      "reused": true,
      "local_path": "attachments/4ae466d6e193/images/001.jpg",
      "src": "https://b2633864.assetcdn.net/2633864/wp-content/uploads/2017/05/face_alignment_angle.jpg?lossy=2&strip=1&webp=1",
      "alt": "",
      "downloaded": true
    },
    {
      "status": "ok",
      "url": "https://b2633864.assetcdn.net/2633864/wp-content/uploads/2017/05/face_alignment_midpoint.jpg?lossy=2&strip=1&webp=1",
      "bytes": 18294,
      "reused": true,
      "local_path": "attachments/4ae466d6e193/images/003.jpg",
      "src": "https://b2633864.assetcdn.net/2633864/wp-content/uploads/2017/05/face_alignment_midpoint.jpg?lossy=2&strip=1&webp=1",
      "alt": "",
      "downloaded": true
    },
    {
      "status": "ok",
      "url": "https://b2633864.assetcdn.net/2633864/wp-content/uploads/2017/05/face_alignment_example_01.jpg?lossy=2&strip=1&webp=1",
      "bytes": 43751,
      "reused": false,
      "local_path": "attachments/4ae466d6e193/images/body-38d441fab85a9c0e369c.jpg",
      "src": "https://b2633864.assetcdn.net/2633864/wp-content/uploads/2017/05/face_alignment_example_01.jpg?lossy=2&strip=1&webp=1",
      "alt": "",
      "downloaded": true
    },
    {
      "status": "ok",
      "url": "https://b2633864.assetcdn.net/2633864/wp-content/uploads/2017/05/face_alignment_example_01_result_01.jpg?lossy=2&strip=1&webp=1",
      "bytes": 19557,
      "reused": true,
      "local_path": "attachments/4ae466d6e193/images/007.jpg",
      "src": "https://b2633864.assetcdn.net/2633864/wp-content/uploads/2017/05/face_alignment_example_01_result_01.jpg?lossy=2&strip=1&webp=1",
      "alt": "",
      "downloaded": true
    },
    {
      "status": "ok",
      "url": "https://b2633864.assetcdn.net/2633864/wp-content/uploads/2017/05/face_alignment_example_01_result_02.jpg?lossy=2&strip=1&webp=1",
      "bytes": 20458,
      "reused": true,
      "local_path": "attachments/4ae466d6e193/images/009.jpg",
      "src": "https://b2633864.assetcdn.net/2633864/wp-content/uploads/2017/05/face_alignment_example_01_result_02.jpg?lossy=2&strip=1&webp=1",
      "alt": "",
      "downloaded": true
    },
    {
      "status": "ok",
      "url": "https://b2633864.assetcdn.net/2633864/wp-content/uploads/2017/05/face_alignment_example_02.jpg?lossy=2&strip=1&webp=1",
      "bytes": 37744,
      "reused": false,
      "local_path": "attachments/4ae466d6e193/images/body-7ae59d4991cddcb66108.jpg",
      "src": "https://b2633864.assetcdn.net/2633864/wp-content/uploads/2017/05/face_alignment_example_02.jpg?lossy=2&strip=1&webp=1",
      "alt": "",
      "downloaded": true
    },
    {
      "status": "ok",
      "url": "https://b2633864.assetcdn.net/2633864/wp-content/uploads/2017/05/face_alignment_example_02_result_01.jpg?lossy=2&strip=1&webp=1",
      "bytes": 16631,
      "reused": true,
      "local_path": "attachments/4ae466d6e193/images/013.jpg",
      "src": "https://b2633864.assetcdn.net/2633864/wp-content/uploads/2017/05/face_alignment_example_02_result_01.jpg?lossy=2&strip=1&webp=1",
      "alt": "",
      "downloaded": true
    },
    {
      "status": "ok",
      "url": "https://b2633864.assetcdn.net/2633864/wp-content/uploads/2017/05/face_alignment_example_03.jpg?lossy=2&strip=1&webp=1",
      "bytes": 58708,
      "reused": false,
      "local_path": "attachments/4ae466d6e193/images/body-b961222bbb20f51c589a.jpg",
      "src": "https://b2633864.assetcdn.net/2633864/wp-content/uploads/2017/05/face_alignment_example_03.jpg?lossy=2&strip=1&webp=1",
      "alt": "",
      "downloaded": true
    },
    {
      "status": "ok",
      "url": "https://b2633864.assetcdn.net/2633864/wp-content/uploads/2017/05/face_alignment_example_03_result_01.jpg?lossy=2&strip=1&webp=1",
      "bytes": 19803,
      "reused": true,
      "local_path": "attachments/4ae466d6e193/images/017.jpg",
      "src": "https://b2633864.assetcdn.net/2633864/wp-content/uploads/2017/05/face_alignment_example_03_result_01.jpg?lossy=2&strip=1&webp=1",
      "alt": "",
      "downloaded": true
    },
    {
      "status": "ok",
      "url": "https://b2633864.assetcdn.net/2633864/wp-content/uploads/2017/05/face_alignment_example_03_result_02.jpg?lossy=2&strip=1&webp=1",
      "bytes": 19319,
      "reused": true,
      "local_path": "attachments/4ae466d6e193/images/019.jpg",
      "src": "https://b2633864.assetcdn.net/2633864/wp-content/uploads/2017/05/face_alignment_example_03_result_02.jpg?lossy=2&strip=1&webp=1",
      "alt": "",
      "downloaded": true
    },
    {
      "status": "ok",
      "url": "https://b2633864.assetcdn.net/2633864/wp-content/uploads/2017/05/face_alignment_example_04.jpg?lossy=2&strip=1&webp=1",
      "bytes": 36406,
      "reused": false,
      "local_path": "attachments/4ae466d6e193/images/body-acfcca701bc957d77acf.jpg",
      "src": "https://b2633864.assetcdn.net/2633864/wp-content/uploads/2017/05/face_alignment_example_04.jpg?lossy=2&strip=1&webp=1",
      "alt": "",
      "downloaded": true
    },
    {
      "status": "ok",
      "url": "https://b2633864.assetcdn.net/2633864/wp-content/uploads/2017/05/face_alignment_example_04_result_01.jpg?lossy=2&strip=1&webp=1",
      "bytes": 20933,
      "reused": true,
      "local_path": "attachments/4ae466d6e193/images/023.jpg",
      "src": "https://b2633864.assetcdn.net/2633864/wp-content/uploads/2017/05/face_alignment_example_04_result_01.jpg?lossy=2&strip=1&webp=1",
      "alt": "",
      "downloaded": true
    },
    {
      "status": "ok",
      "url": "https://b2633864.assetcdn.net/2633864/wp-content/uploads/2017/05/face_alignment_example_04_result_02.jpg?lossy=2&strip=1&webp=1",
      "bytes": 21633,
      "reused": true,
      "local_path": "attachments/4ae466d6e193/images/025.jpg",
      "src": "https://b2633864.assetcdn.net/2633864/wp-content/uploads/2017/05/face_alignment_example_04_result_02.jpg?lossy=2&strip=1&webp=1",
      "alt": "",
      "downloaded": true
    },
    {
      "status": "ok",
      "url": "https://b2633864.assetcdn.net/2633864/wp-content/uploads/2017/05/face_alignment_example_05.jpg?lossy=2&strip=1&webp=1",
      "bytes": 38675,
      "reused": false,
      "local_path": "attachments/4ae466d6e193/images/body-63d3b7908b903c39d6bf.jpg",
      "src": "https://b2633864.assetcdn.net/2633864/wp-content/uploads/2017/05/face_alignment_example_05.jpg?lossy=2&strip=1&webp=1",
      "alt": "",
      "downloaded": true
    },
    {
      "status": "ok",
      "url": "https://b2633864.assetcdn.net/2633864/wp-content/uploads/2017/05/face_alignment_example_05_result_01.jpg?lossy=2&strip=1&webp=1",
      "bytes": 17997,
      "reused": true,
      "local_path": "attachments/4ae466d6e193/images/029.jpg",
      "src": "https://b2633864.assetcdn.net/2633864/wp-content/uploads/2017/05/face_alignment_example_05_result_01.jpg?lossy=2&strip=1&webp=1",
      "alt": "",
      "downloaded": true
    },
    {
      "status": "ok",
      "url": "https://b2633864.assetcdn.net/2633864/wp-content/uploads/2017/05/face_alignment_example_05_result_02.jpg?lossy=2&strip=1&webp=1",
      "bytes": 17515,
      "reused": true,
      "local_path": "attachments/4ae466d6e193/images/031.jpg",
      "src": "https://b2633864.assetcdn.net/2633864/wp-content/uploads/2017/05/face_alignment_example_05_result_02.jpg?lossy=2&strip=1&webp=1",
      "alt": "",
      "downloaded": true
    },
    {
      "status": "ok",
      "url": "https://fast.wistia.com/embed/medias/kno0cmko2z/swatch",
      "bytes": 3143,
      "reused": false,
      "local_path": "attachments/4ae466d6e193/images/body-8ec9834d54100244f57f.img",
      "src": "https://fast.wistia.com/embed/medias/kno0cmko2z/swatch",
      "alt": "",
      "downloaded": true
    },
    {
      "status": "ok",
      "url": "https://b2633864.assetcdn.net/2633864/wp-content/uploads/2020/01/cta-source-guide-1.png?lossy=2&strip=1&webp=1",
      "bytes": 28126,
      "reused": false,
      "local_path": "attachments/4ae466d6e193/images/body-8cd5816cf73385d96fcd.png",
      "src": "https://b2633864.assetcdn.net/2633864/wp-content/uploads/2020/01/cta-source-guide-1.png?lossy=2&strip=1&webp=1",
      "alt": "",
      "downloaded": true
    }
  ],
  "audio": [],
  "videos": [],
  "links": [
    {
      "href": "https://pyimagesearch.com/2017/04/03/facial-landmarks-dlib-opencv-python/",
      "label": "facial landmarks",
      "type": "a"
    },
    {
      "href": "https://pyimagesearch.com/pyimagesearch-gurus/",
      "label": "PyImageSearch Gurus course",
      "type": "a"
    },
    {
      "href": "https://github.com/jrosebr1/imutils/blob/master/imutils/face_utils/facealigner.py",
      "label": "FaceAligner class in imutils",
      "type": "a"
    },
    {
      "href": "http://amzn.to/2qZAr4e",
      "label": "Mastering OpenCV with Practical Computer Vision Projects",
      "type": "a"
    },
    {
      "href": "https://github.com/MasteringOpenCV/code/blob/master/Chapter8_FaceRecognition/preprocessFace.cpp",
      "label": "on GitHub",
      "type": "a"
    },
    {
      "href": "http://docs.opencv.org/trunk/da/d6e/tutorial_py_geometric_transformations.html",
      "label": "the OpenCV documentation",
      "type": "a"
    },
    {
      "href": "https://pyimagesearch.com/2017/03/27/how-to-install-dlib/",
      "label": "installed on your system",
      "type": "a"
    },
    {
      "href": "https://pyimagesearch.com/opencv-tutorials-resources-guides/",
      "label": "OpenCV install tutorials",
      "type": "a"
    },
    {
      "href": "https://pyimagesearch.com/2014/11/10/histogram-oriented-gradients-object-detection/",
      "label": "Histogram of Oriented Gradients",
      "type": "a"
    },
    {
      "href": "https://pyimagesearch.com/pyimagesearch-university/?utm_source=blogPost&utm_medium=bottomBanner&utm_campaign=What%27s%20next%3F%20I%20recommend",
      "label": "PyImageSearch University",
      "type": "a"
    }
  ],
  "repair_version": 2,
  "repaired_at": "2026-10-14T10:54:12Z",
  "source_url": "https://pyimagesearch.com/2017/05/22/face-alignment-with-opencv-and-python/",
  "papers": [],
  "actual_video_files": [
    {
      "local_path": "attachments/4ae466d6e193/videos/kno0cmko2z.mp4",
      "bytes": 154424456,
      "valid": true,
      "height": 1080,
      "width": 1920,
      "duration": "231.500000",
      "status": "ok"
    }
  ],
  "body_chars": 24587,
  "expected_body_images": 20,
  "discovery_status": "ok",
  "delivery_status": "ok",
  "missing_reason": "",
  "raw_html": "attachments/4ae466d6e193/pages/source.html",
  "local_html": "attachments/4ae466d6e193/pages/article.html",
  "body_base_path": "attachments/4ae466d6e193/pages/",
  "body_markdown_path": "attachments/4ae466d6e193/pages/article.md"
}