Many methods in signal processing and numerical linear algebra need a set of vectors that are perpendicular to each other. The vectors you are given are usually not. The Gram Schmidt process builds a perpendicular set from them, one vector at a time, and keeps the space they span. I'll first show what goes in and what comes out. Then we'll go through the formulas, read each term as a projection, and work a 3 x 3 example by hand. The last sections show how to check the result and how the process leads to an orthonormal matrix and the QR decomposition.
- What does orthogonalization produce ?
- What are the steps of the process ?
- What does each step mean ?
- How is the process written with projections ?
- Example
- How can you be sure that the result is orthogonal ?
- How do you get an orthonormal matrix and the QR decomposition ?
What does orthogonalization produce ?
Matrix Orthogonalization is a process of deriving a Orthogonal Matrix from a non-orthogonal matrix. This can be applied to both column vectors and row vectors as illustrated below. (Note that the derived matrix may maintain a certain properties of the orignal matrix, but they are not exactly same).
The picture below treats each row of a matrix as one vector. The input matrix (A) has rows x1, x2 and x3 that are not perpendicular to each other. The output matrix (B) has rows v1, v2 and v3, and every pair of them is perpendicular.

Figure 1. Orthogonalization of row vectors. The rows x1, x2, x3 become mutually perpendicular rows v1, v2, v3.
The picture below applies exactly the same idea to the columns. Nothing in the formulas changes. Only the way you read the vectors out of the matrix changes.

Figure 2. Orthogonalization of column vectors. The same process works on columns, which is the form used by the QR decomposition.
The span is kept : v1 to vk span the same space as x1 to xk for every k. This is the property the result shares with the original matrix.The vectors themselves change : only v1 equals its original. Every later vector loses its components along the earlier ones, so lengths and directions differ from the input.Orthogonal rows do not yet make an orthogonal matrix : in the strict sense, an orthogonal matrix Q has Q QT equal to the identity matrix, so its rows must also have length 1. The process gives perpendicular vectors, and a final normalization step gives unit length.A square determinant survives : each vk is xk minus a combination of earlier vectors, so V = L A with L lower triangular and ones on its diagonal. Therefore det(V) = det(A).
What are the steps of the process ?
Following is the process showing you derives the orthogonal matrix. If you just want to have algorithm, just copy this one as you need. But if you really want to understand the meaning of each step and how this process works, refer to Vector projection onto a Line first. Then these procedure would make more sense to you.
The formulas below handle three vectors. The first vector is taken as it is. Each later vector is the original minus one correction term for every vector already produced. So v2 has one correction term, and v3 has two. The pictures write a dot product with a centred dot. In the text of this page the vectors are rows, so the same dot product of x and v is written x vT, as in the example later on.

Figure 3. Gram Schmidt formulas for three vectors. Each new vector subtracts one correction term per vector already built.
The next formula is the general step for the n-th vector. It has n-1 correction terms, one for each of v1 to vn-1.

Figure 4. General Gram Schmidt step. The n-th vector subtracts its projection onto every earlier vector.
Read xn where the formula shows x3 : Figure 4 starts with x3 and uses x3 in the first two numerators. The correct general step is vn = xn - (xnv1T)/(v1v1T) v1 - ... - (xnvn-1T)/(vn-1vn-1T) vn-1. The last term in the figure already has the correct xn.The numerator always uses the original vector : xnvjT takes the input vector xn, while the denominator vjvjT takes the new vector vj.A zero vector means dependence : if some vn comes out as the zero vector, xn is a combination of the earlier inputs. Skip it, because the next step would divide by vnvnT = 0.The order of the inputs matters : a different order gives a different set of perpendicular vectors. Only the first vector is kept unchanged.
What does each step mean ?
The formulas are easy to copy but hard to remember. They become easy to remember once each correction term is read as a projection, which is the length of one vector along the direction of another. The three pictures below take the formulas one line at a time.
If you look into the mathematical meaning of each step, it can be described as shown below.

Figure 5. The first step. The input x1 is the reference direction and is copied to v1 without change.
The second step, below, marks the correction term as the projection of x2 onto v1. Subtracting it leaves the part of x2 that is perpendicular to v1.

Figure 6. The second step. Removing the projection of x2 onto v1 leaves a vector perpendicular to v1.
The third step, below, removes two projections, one onto v1 and one onto v2. The first bracket leaves a vector perpendicular to v1, and the second bracket makes it perpendicular to v2 as well.

Figure 7. The third step. Removing both projections leaves a vector perpendicular to v1 and to v2.
The subtraction is what creates the right angle : a vector minus its projection onto vj has a zero dot product with vj. You can check this by taking the dot product of both sides of the v2 formula with v1.One removal does not disturb another : v1 and v2 are already perpendicular. So removing the part along v2 adds nothing back along v1, and the two corrections can be done in either order."Starting from the end of x" describes the drawing : the annotations picture the correction as an arrow drawn from the tip of x2 or x3. The result vn itself is still a vector from the origin.
How is the process written with projections ?
Textbooks usually write the process with a projection operator, because it hides the fractions and shows the structure. The operator proju(x) = (xuT)/(uuT) u is the projection of x onto the line along u.
If you look into the concept of vector projection onto a line, you would easily understand that this process can be described as shown below.

Figure 8. The process in projection form. Each new vector is its original minus the projections onto all earlier vectors.
The v3 line should project x3 twice : Figure 8 writes projv1(x2) in the v3 line. The correct line is v3 = x3 - projv1(x3) - projv2(x3), which matches Figure 3.The general line should project xn onto vj : Figure 8 writes projuj(vn) inside the sum. The correct form is vn = xn - Σ projvj(xn), with j running from 1 to n-1. The vector being projected is the input xn, and the direction is the new vector vj.The sum is a projection onto a subspace : the terms together give the projection of xn onto the space spanned by v1 to vn-1. So vn is the part of xn that the earlier vectors cannot represent.
Example
A small example with simple numbers is the best way to see every fraction appear. Watch the denominators in particular, because they come from the new vectors vj and not from the inputs.
Let's assume that we are given a matrix as follows and assume that we want to orthogonalize the row vectors

Figure 9. The example matrix. Its determinant is 1, so the three rows are independent and the process gives three nonzero vectors.
At first, let's take out each row as vectors as follows.

Figure 10. The inputs x1 = [1 -1 1], x2 = [1 0 1] and x3 = [1 1 2].
Now just plug in these vectors into each procedures and you will get the following result. (Note that the second vector in each inner product is transposed. This is to make inner product possible and produce a scalar value)

Figure 11. The worked calculation. The result is v1 = [1 -1 1], v2 = [1/3 2/3 1/3] and v3 = [-1/2 0 1/2].
v2 needs one fraction : x2v1T = 2 and v1v1T = 3. So v2 = [1 0 1] - (2/3)[1 -1 1] = [1/3 2/3 1/3].v3 needs two fractions : x3v1T = 2 over v1v1T = 3 gives 2/3. Then x3v2T = 5/3 over v2v2T = 2/3 gives 5/2. So v3 = [1 1 2] - (2/3)[1 -1 1] - (5/2)[1/3 2/3 1/3] = [-1/2 0 1/2].The second line of the v3 expansion should start from x3 : Figure 11 writes [1 0 1], which is x2, as the first vector and in the first numerator. Both should be x3 = [1 1 2]. The final answer [-1/2 0 1/2] is correct, and it is what the correct substitution gives.
How can you be sure that the result is orthogonal ?
A hand calculation needs its own check, because one wrong fraction makes every later vector wrong. The error also stays hidden, since the wrong result still looks like a plausible set of vectors.
Now we have completed the calculation. Then you may ask how we can be sure that the answer is correct ?
One way to prove it is to draw the resulting vectors into a coordinate and check if the angles between each vector are 90 degree.
The plots below draw the inputs on the left and the results on the right, all starting from the origin. On the right, v1 is the longest vector, and v2 and v3 are much shorter, because their lengths are √(2/3) and √(1/2).

Figure 12. Input vectors on the left and orthogonalized vectors on the right. A 3D view makes the right angles hard to judge by eye.
However this kind of graphical method can be an easy and quick method in 2 vectors (2 Dimensional), but in 3 vectors, it is difficult to measure the angles between vectors correctly. If it goes more than 3, it is impossible to apply this kind of graphical method.
More accurate way is to take the innerproduct of all the two combination from the resulting matrix. In case of this example, take the following inner product. If you get '0' for all of these, it means that you derived a correct orthogonal matrix.

Figure 13. The three pairwise inner products to check. All three are 0 for this example.
v1v2T = 0 : 1/3 - 2/3 + 1/3 = 0.v1v3T = 0 : -1/2 + 0 + 1/2 = 0.v2v3T = 0 : -1/6 + 0 + 1/6 = 0.One matrix product does all the checks at once : with V holding v1, v2, v3 as rows, V VT must be diagonal. Here it is diag(3, 2/3, 1/2). The diagonal holds the squared lengths, and every off-diagonal entry is an inner product that must be 0.A computer result is zero only up to rounding : in floating point, expect values such as 1e-16 rather than exact zeros. Compare against a small tolerance.
How do you get an orthonormal matrix and the QR decomposition ?
Most applications need unit vectors as well as right angles. So the usual last step divides each vk by its length. The resulting set is orthonormal, and the matrix that holds it is an orthogonal matrix in the strict sense.
For the example, the lengths are √3, √(2/3) and √(1/2). The normalized rows are therefore q1 = [1 -1 1]/√3, q2 = [1 2 1]/√6 and q3 = [-1 0 1]/√2. With these rows in Q, Q QT = I holds exactly.
Keeping the fractions you computed along the way gives more than Q. Write the inputs as columns of AT. Then AT = QT R, where R is upper triangular. For this example, the rows of R are [√3 2/√3 2/√3], [0 √(2/3) 5/√6] and [0 0 1/√2]. This is the QR decomposition, and Gram Schmidt is the most direct way to compute it by hand.
R records the projections : each entry above the diagonal of R is xnqjT, the length of an input along an earlier unit vector. Each diagonal entry is the length of vn.Library QR may flip signs : numpy.linalg.qr on the same columns returns the same q vectors up to sign. For this matrix it negates q1 and q2 and the matching rows of R. Both answers are valid.Classical Gram Schmidt loses accuracy on nearly dependent vectors : rounding errors pile up, and the computed vectors drift away from perpendicular. Modified Gram Schmidt, which removes each projection from the running vector, or a Householder QR is the usual choice in numerical code.The determinant check still works : det(A) = 1 here, and the product of the three lengths √3 x √(2/3) x √(1/2) is also 1.