Skip to content

Instantly share code, notes, and snippets.

@alisterburt
Created July 8, 2025 20:43
Show Gist options
  • Select an option

  • Save alisterburt/68b3e5ddb2e796483fd0f5578ae28f8f to your computer and use it in GitHub Desktop.

Select an option

Save alisterburt/68b3e5ddb2e796483fd0f5578ae28f8f to your computer and use it in GitHub Desktop.
how to reason about RELION's Euler angles
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"metadata": {},
"cell_type": "markdown",
"source": [
"goal: try to understand physical meaning of RELION's Euler angles\n",
"\n",
"- RELION eulers are ZYZ intrinsic euler angles\n",
"- these eulers rotate coordinates and sample at rotated positions\n",
"- the above has the effect of inverting the transformation\n",
"- inverting a rotation matrix is taking its transpose because it's orthogonal"
],
"id": "2cf5d4299314cf62"
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2025-07-08T20:42:26.999542Z",
"start_time": "2025-07-08T20:42:26.712185Z"
}
},
"cell_type": "code",
"source": [
"import sympy\n",
"from sympy import cos, sin, pi, Matrix, symbols, simplify"
],
"id": "21f03dd7ba4265b0",
"outputs": [],
"execution_count": 1
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2025-07-08T20:42:27.094804Z",
"start_time": "2025-07-08T20:42:27.092535Z"
}
},
"cell_type": "code",
"source": [
"# define rotation matrices\n",
"\n",
"# Define angle symbols\n",
"rot, tilt, psi = symbols('α β γ', real=True)\n",
"\n",
"# Rotation matrix about X-axis\n",
"def Rx(theta):\n",
" return Matrix([\n",
" [1, 0, 0],\n",
" [0, cos(theta), -sin(theta)],\n",
" [0, sin(theta), cos(theta)]\n",
" ])\n",
"\n",
"# Rotation matrix about Y-axis\n",
"def Ry(theta):\n",
" return Matrix([\n",
" [cos(theta), 0, sin(theta)],\n",
" [0, 1, 0],\n",
" [-sin(theta), 0, cos(theta)]\n",
" ])\n",
"\n",
"# Rotation matrix about Z-axis\n",
"def Rz(theta):\n",
" return Matrix([\n",
" [cos(theta), -sin(theta), 0],\n",
" [sin(theta), cos(theta), 0],\n",
" [0, 0, 1]\n",
" ])\n"
],
"id": "ad5dd2c64324c8fc",
"outputs": [],
"execution_count": 2
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2025-07-08T20:42:27.174141Z",
"start_time": "2025-07-08T20:42:27.103854Z"
}
},
"cell_type": "code",
"source": [
"# intrinsic rotation\n",
"# rotate around z by rot\n",
"# rotate around new y by tilt\n",
"# rotate around new z by psi\n",
"zyz_intrinsic = Rz(rot) * Ry(tilt) * Rz(psi)\n",
"zyz_intrinsic"
],
"id": "448c5920bffeeb14",
"outputs": [
{
"data": {
"text/plain": [
"Matrix([\n",
"[-sin(α)*sin(γ) + cos(α)*cos(β)*cos(γ), -sin(α)*cos(γ) - sin(γ)*cos(α)*cos(β), sin(β)*cos(α)],\n",
"[ sin(α)*cos(β)*cos(γ) + sin(γ)*cos(α), -sin(α)*sin(γ)*cos(β) + cos(α)*cos(γ), sin(α)*sin(β)],\n",
"[ -sin(β)*cos(γ), sin(β)*sin(γ), cos(β)]])"
],
"text/latex": "$\\displaystyle \\left[\\begin{matrix}- \\sin{\\left(α \\right)} \\sin{\\left(γ \\right)} + \\cos{\\left(α \\right)} \\cos{\\left(β \\right)} \\cos{\\left(γ \\right)} & - \\sin{\\left(α \\right)} \\cos{\\left(γ \\right)} - \\sin{\\left(γ \\right)} \\cos{\\left(α \\right)} \\cos{\\left(β \\right)} & \\sin{\\left(β \\right)} \\cos{\\left(α \\right)}\\\\\\sin{\\left(α \\right)} \\cos{\\left(β \\right)} \\cos{\\left(γ \\right)} + \\sin{\\left(γ \\right)} \\cos{\\left(α \\right)} & - \\sin{\\left(α \\right)} \\sin{\\left(γ \\right)} \\cos{\\left(β \\right)} + \\cos{\\left(α \\right)} \\cos{\\left(γ \\right)} & \\sin{\\left(α \\right)} \\sin{\\left(β \\right)}\\\\- \\sin{\\left(β \\right)} \\cos{\\left(γ \\right)} & \\sin{\\left(β \\right)} \\sin{\\left(γ \\right)} & \\cos{\\left(β \\right)}\\end{matrix}\\right]$"
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 3
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2025-07-08T20:42:27.189695Z",
"start_time": "2025-07-08T20:42:27.183098Z"
}
},
"cell_type": "code",
"source": [
"# extrinsic rotation\n",
"# rotate around z by rot\n",
"# rotate around original y by tilt\n",
"# rotate around original z by psi\n",
"# different output\n",
"zyz_extrinsic = Rz(psi) * Ry(tilt) * Rz(rot)\n",
"zyz_extrinsic"
],
"id": "1e301398648b24d",
"outputs": [
{
"data": {
"text/plain": [
"Matrix([\n",
"[-sin(α)*sin(γ) + cos(α)*cos(β)*cos(γ), -sin(α)*cos(β)*cos(γ) - sin(γ)*cos(α), sin(β)*cos(γ)],\n",
"[ sin(α)*cos(γ) + sin(γ)*cos(α)*cos(β), -sin(α)*sin(γ)*cos(β) + cos(α)*cos(γ), sin(β)*sin(γ)],\n",
"[ -sin(β)*cos(α), sin(α)*sin(β), cos(β)]])"
],
"text/latex": "$\\displaystyle \\left[\\begin{matrix}- \\sin{\\left(α \\right)} \\sin{\\left(γ \\right)} + \\cos{\\left(α \\right)} \\cos{\\left(β \\right)} \\cos{\\left(γ \\right)} & - \\sin{\\left(α \\right)} \\cos{\\left(β \\right)} \\cos{\\left(γ \\right)} - \\sin{\\left(γ \\right)} \\cos{\\left(α \\right)} & \\sin{\\left(β \\right)} \\cos{\\left(γ \\right)}\\\\\\sin{\\left(α \\right)} \\cos{\\left(γ \\right)} + \\sin{\\left(γ \\right)} \\cos{\\left(α \\right)} \\cos{\\left(β \\right)} & - \\sin{\\left(α \\right)} \\sin{\\left(γ \\right)} \\cos{\\left(β \\right)} + \\cos{\\left(α \\right)} \\cos{\\left(γ \\right)} & \\sin{\\left(β \\right)} \\sin{\\left(γ \\right)}\\\\- \\sin{\\left(β \\right)} \\cos{\\left(α \\right)} & \\sin{\\left(α \\right)} \\sin{\\left(β \\right)} & \\cos{\\left(β \\right)}\\end{matrix}\\right]$"
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 4
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2025-07-08T20:42:27.223121Z",
"start_time": "2025-07-08T20:42:27.216124Z"
}
},
"cell_type": "code",
"source": [
"# actual equivalent extrinsic rotation\n",
"# rotate around z by psi\n",
"# rotate around original y by tilt\n",
"# rotate around original z by rot\n",
"equivalent_zyz_extrinsic = Rz(rot) * Ry(tilt) * Rz(psi)\n",
"equivalent_zyz_extrinsic"
],
"id": "eebe494c6ce55c2f",
"outputs": [
{
"data": {
"text/plain": [
"Matrix([\n",
"[-sin(α)*sin(γ) + cos(α)*cos(β)*cos(γ), -sin(α)*cos(γ) - sin(γ)*cos(α)*cos(β), sin(β)*cos(α)],\n",
"[ sin(α)*cos(β)*cos(γ) + sin(γ)*cos(α), -sin(α)*sin(γ)*cos(β) + cos(α)*cos(γ), sin(α)*sin(β)],\n",
"[ -sin(β)*cos(γ), sin(β)*sin(γ), cos(β)]])"
],
"text/latex": "$\\displaystyle \\left[\\begin{matrix}- \\sin{\\left(α \\right)} \\sin{\\left(γ \\right)} + \\cos{\\left(α \\right)} \\cos{\\left(β \\right)} \\cos{\\left(γ \\right)} & - \\sin{\\left(α \\right)} \\cos{\\left(γ \\right)} - \\sin{\\left(γ \\right)} \\cos{\\left(α \\right)} \\cos{\\left(β \\right)} & \\sin{\\left(β \\right)} \\cos{\\left(α \\right)}\\\\\\sin{\\left(α \\right)} \\cos{\\left(β \\right)} \\cos{\\left(γ \\right)} + \\sin{\\left(γ \\right)} \\cos{\\left(α \\right)} & - \\sin{\\left(α \\right)} \\sin{\\left(γ \\right)} \\cos{\\left(β \\right)} + \\cos{\\left(α \\right)} \\cos{\\left(γ \\right)} & \\sin{\\left(α \\right)} \\sin{\\left(β \\right)}\\\\- \\sin{\\left(β \\right)} \\cos{\\left(γ \\right)} & \\sin{\\left(β \\right)} \\sin{\\left(γ \\right)} & \\cos{\\left(β \\right)}\\end{matrix}\\right]$"
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 5
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2025-07-08T20:42:31.444352Z",
"start_time": "2025-07-08T20:42:27.243901Z"
}
},
"cell_type": "code",
"source": [
"# inverse to account for fact that we are defining a transform applied to\n",
"# the internal coordinate system of an image\n",
"zyz_intrinsic_inverse = zyz_intrinsic.inv()\n",
"simplify(zyz_intrinsic_inverse)"
],
"id": "a0f4f7746bde59ab",
"outputs": [
{
"data": {
"text/plain": [
"Matrix([\n",
"[-sin(α)*sin(γ) + cos(α)*cos(β)*cos(γ), sin(α)*cos(β)*cos(γ) + sin(γ)*cos(α), -sin(β)*cos(γ)],\n",
"[-sin(α)*cos(γ) - sin(γ)*cos(α)*cos(β), -sin(α)*sin(γ)*cos(β) + cos(α)*cos(γ), sin(β)*sin(γ)],\n",
"[ sin(β)*cos(α), sin(α)*sin(β), cos(β)]])"
],
"text/latex": "$\\displaystyle \\left[\\begin{matrix}- \\sin{\\left(α \\right)} \\sin{\\left(γ \\right)} + \\cos{\\left(α \\right)} \\cos{\\left(β \\right)} \\cos{\\left(γ \\right)} & \\sin{\\left(α \\right)} \\cos{\\left(β \\right)} \\cos{\\left(γ \\right)} + \\sin{\\left(γ \\right)} \\cos{\\left(α \\right)} & - \\sin{\\left(β \\right)} \\cos{\\left(γ \\right)}\\\\- \\sin{\\left(α \\right)} \\cos{\\left(γ \\right)} - \\sin{\\left(γ \\right)} \\cos{\\left(α \\right)} \\cos{\\left(β \\right)} & - \\sin{\\left(α \\right)} \\sin{\\left(γ \\right)} \\cos{\\left(β \\right)} + \\cos{\\left(α \\right)} \\cos{\\left(γ \\right)} & \\sin{\\left(β \\right)} \\sin{\\left(γ \\right)}\\\\\\sin{\\left(β \\right)} \\cos{\\left(α \\right)} & \\sin{\\left(α \\right)} \\sin{\\left(β \\right)} & \\cos{\\left(β \\right)}\\end{matrix}\\right]$"
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 6
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2025-07-08T20:42:31.555659Z",
"start_time": "2025-07-08T20:42:31.458695Z"
}
},
"cell_type": "code",
"source": [
"# compute inverse with negative arguments and opposite order of axes\n",
"# just to demonstrate this is the same as the analytical inverse above\n",
"direct_inverse = Rz(-psi) * Ry(-tilt) * Rz(-rot)\n",
"simplify(direct_inverse)"
],
"id": "e2ac13d79978af6",
"outputs": [
{
"data": {
"text/plain": [
"Matrix([\n",
"[-sin(α)*sin(γ) + cos(α)*cos(β)*cos(γ), sin(α)*cos(β)*cos(γ) + sin(γ)*cos(α), -sin(β)*cos(γ)],\n",
"[-sin(α)*cos(γ) - sin(γ)*cos(α)*cos(β), -sin(α)*sin(γ)*cos(β) + cos(α)*cos(γ), sin(β)*sin(γ)],\n",
"[ sin(β)*cos(α), sin(α)*sin(β), cos(β)]])"
],
"text/latex": "$\\displaystyle \\left[\\begin{matrix}- \\sin{\\left(α \\right)} \\sin{\\left(γ \\right)} + \\cos{\\left(α \\right)} \\cos{\\left(β \\right)} \\cos{\\left(γ \\right)} & \\sin{\\left(α \\right)} \\cos{\\left(β \\right)} \\cos{\\left(γ \\right)} + \\sin{\\left(γ \\right)} \\cos{\\left(α \\right)} & - \\sin{\\left(β \\right)} \\cos{\\left(γ \\right)}\\\\- \\sin{\\left(α \\right)} \\cos{\\left(γ \\right)} - \\sin{\\left(γ \\right)} \\cos{\\left(α \\right)} \\cos{\\left(β \\right)} & - \\sin{\\left(α \\right)} \\sin{\\left(γ \\right)} \\cos{\\left(β \\right)} + \\cos{\\left(α \\right)} \\cos{\\left(γ \\right)} & \\sin{\\left(β \\right)} \\sin{\\left(γ \\right)}\\\\\\sin{\\left(β \\right)} \\cos{\\left(α \\right)} & \\sin{\\left(α \\right)} \\sin{\\left(β \\right)} & \\cos{\\left(β \\right)}\\end{matrix}\\right]$"
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 7
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2025-07-08T20:42:31.569823Z",
"start_time": "2025-07-08T20:42:31.567720Z"
}
},
"cell_type": "code",
"source": [
"# so if the above transform, the inverse of the zyz intrinsic, is what's applied to our object\n",
"# how to interpret the transformation in terms of euler angles?\n",
"\n",
"# answer:\n",
"# - extrinsic Z(-rot) -> Y(-tilt) -> Z(-psi)\n",
"# - intrinsic Z(-psi) -> Y'(-tilt) -> Z''(-rot)"
],
"id": "3565c706c3cf0aec",
"outputs": [],
"execution_count": 8
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 2
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython2",
"version": "2.7.6"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment