Skip to content

Commit d5c633e

Browse files
kwagyemanclaude
andcommitted
docs: audit ndarray chapter -- style cleanup, split numerical into two pages
Sweep for accumulated style rules across both image and ndarray chapters: italic-before-colon, the "shape" word in non-geometric contexts, "firmware" word, "worked example" phrasing, thousands separators in prose, structural forward references ("the next page"), comma-separated imports, second-person "you" in section headers and prose, and the "framebuffer" / "frame buffer" inconsistency. Cleaned up :py:meth: / :py:class: domain-prefix inconsistencies and a malformed cross-reference with the signature inside the role target. Verified accuracy against the library reference for flatten, linspace, logspace, byteswap, arange, meshgrid, ndinfo, empty, set_printoptions, bincount, nonzero, argsort, array, and the bool / complex dtype widths. Split numerical.rst into two pages so the chapter ends with the wrap-up on an even section number (9.20): "Curves and integration" keeps interp / polyfit / polyval / convolve / trapz; new "Solvers and random numbers" page covers scipy.integrate (quad / romberg / simpson / tanhsinh), scipy.optimize (bisect / newton / fmin), scipy.special (erf / erfc / gamma / gammaln), and numpy.random.Generator. Renamed the toctree caption to "Numerical tools". Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent d99674e commit d5c633e

18 files changed

Lines changed: 339 additions & 268 deletions

docs/openmvcam/tutorial/numpy/basics/dtypes.rst

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,17 @@ dtype bytes range
1919
============= ======= ===============================
2020
``uint8`` 1 0 to 255
2121
``int8`` 1 -128 to 127
22-
``uint16`` 2 0 to 65 535
23-
``int16`` 2 -32 768 to 32 767
22+
``uint16`` 2 0 to 65,535
23+
``int16`` 2 -32,768 to 32,767
2424
``float`` 4 or 8 IEEE 754 (single or double)
2525
``bool`` 1 ``True`` / ``False``
26-
``complex`` 8 or 16 optional, firmware-dependent
26+
``complex`` 8 or 16 optional, build-dependent
2727
============= ======= ===============================
2828

29-
The ``float`` width depends on how the firmware was
29+
The ``float`` width depends on how the cam was
3030
built; ``complex`` is only available on cams whose
31-
firmware reports a ``-c`` suffix in
32-
:data:`ulab.__version__`. There is no ``int32`` or
33-
``int64``.
31+
:data:`ulab.__version__` carries a ``-c`` suffix.
32+
There is no ``int32`` or ``int64``.
3433

3534
Pick the type that matches the hardware that produced the
3635
data. An 8-bit ADC sample wants ``uint8``; a 12-bit ADC
@@ -59,7 +58,7 @@ The dtype of an existing array
5958
:attr:`~ulab.numpy.ndarray.dtype` reads back the array's
6059
dtype as a :class:`ulab.dtype` instance. The single-
6160
character type code is what gets compared on
62-
firmware-conditional code paths::
61+
build-conditional code paths::
6362

6463
a = np.array([1, 2, 3], dtype=np.uint8)
6564
print(a.dtype) # dtype('uint8')

docs/openmvcam/tutorial/numpy/basics/making-arrays.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ along an existing axis::
127127

128128
All inputs must share the same dtype and ``ndim``, and
129129
match on every axis other than the joining one. This is
130-
the right shape for accumulating short buffers into a
130+
the right tool for accumulating short buffers into a
131131
longer one when the final length is known up front; for
132132
the streaming-append pattern see :doc:`../performance`.
133133

docs/openmvcam/tutorial/numpy/basics/the-ndarray.rst

Lines changed: 18 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@ reductions, broadcasting, slicing -- works directly from
4242
those five values plus the data pointer, with no
4343
per-element Python overhead.
4444

45-
What you get from the design
46-
----------------------------
45+
What the design buys
46+
--------------------
4747

4848
Three properties fall out of "packed block + small
4949
descriptor" and define how the rest of the section
@@ -62,7 +62,7 @@ operators all work this way; the universal functions
6262
**Reshape, transpose, and slicing are free.** None of
6363
the three actually moves the data. Each one returns a
6464
new descriptor pointing at the *same* data block. The
65-
result is called a *view*: a second window onto the same
65+
result is called a *view* -- a second window onto the same
6666
underlying buffer. Writing through a view writes to the
6767
source. See :doc:`../shape/views-and-copies`.
6868

@@ -73,8 +73,8 @@ small set of rules that decide which short axis stretches
7373
to match the long one. The stretch is virtual; no data
7474
is duplicated. See :doc:`../math/broadcasting`.
7575

76-
What you give up in exchange
77-
----------------------------
76+
What the design costs
77+
---------------------
7878

7979
Two restrictions follow from the same design.
8080

@@ -86,24 +86,21 @@ page covers the small set of types :mod:`numpy` supports
8686
and the rules that come out of fixing one.
8787

8888
**Growing an array is not free.** A list keeps spare
89-
slots at the end and lets you ``.append`` cheaply. An
89+
slots at the end and supports ``.append`` cheaply. An
9090
:class:`~ulab.numpy.ndarray` is exactly the size it
9191
needs to be; appending would mean allocating a new,
9292
larger buffer and copying the old contents into it.
9393
There is no :meth:`append` method, on purpose. The right
94-
shape on the camera is to pre-allocate the destination
94+
pattern on the camera is to pre-allocate the destination
9595
at its final size and *fill* it; :doc:`../performance`
96-
covers the pattern.
97-
98-
Where to look next
99-
------------------
100-
101-
:doc:`making-arrays` is the constructor catalogue --
102-
every way to actually get an
103-
:class:`~ulab.numpy.ndarray` from a literal, a sensor
104-
buffer, or a generated sequence. :doc:`dtypes` covers
105-
the element-type choice that decides how much RAM each
106-
element costs. After those two,
107-
:doc:`../shape/shape-and-strides` opens the descriptor
108-
back up to explain how multi-dimensional indexing falls
109-
out of it.
96+
covers the technique.
97+
98+
With a packed typed buffer for the data, a small
99+
descriptor for the metadata, and three behavioural
100+
guarantees (fast element-wise math, free reshape /
101+
transpose / slicing, and shapes that broadcast), the
102+
:class:`~ulab.numpy.ndarray` is the foundation the rest
103+
of the chapter rests on. How an array actually comes
104+
into existence -- from a literal, from a pre-filled
105+
allocation, from a peripheral buffer -- is the next
106+
practical question.

docs/openmvcam/tutorial/numpy/basics/why-arrays.rst

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ the numerical work an OpenMV application runs into:
2323
buffer, an IIR filter applied to a sensor's output, a
2424
spectrogram a classifier wants as input.
2525

26-
All of these want the same shape: a buffer of numbers
26+
All of these want the same form: a buffer of numbers
2727
with one operation applied to every element. A Python
2828
``for`` loop is the obvious way to write it::
2929

@@ -40,7 +40,7 @@ costs add up to tens of milliseconds for what is
4040
fundamentally a quick operation.
4141

4242
That overhead bites every time a script reaches a buffer.
43-
A QVGA grayscale frame is 76 800 pixels; an
43+
A QVGA grayscale frame is 76,800 pixels; an
4444
accelerometer at 100 Hz delivers a hundred three-axis
4545
samples a second; a microphone fills a 1024-sample
4646
buffer every 64 ms. A pure-Python ``for`` loop over any
@@ -82,12 +82,12 @@ elsewhere, with the list keeping a reference to it. A
8282
library function looking at a list would still have to
8383
unpack each element through that reference and check its
8484
type -- exactly the cost the loop pays. Lists are the
85-
wrong shape for fast array math.
85+
wrong fit for fast array math.
8686

8787
Why bytearray is not enough either
8888
----------------------------------
8989

90-
A :class:`bytearray` is the right *shape* -- one
90+
A :class:`bytearray` is the right *form* -- one
9191
typed buffer, one byte per element, all in one
9292
contiguous block. It is what most byte-oriented
9393
peripheral APIs hand back. What it lacks is the *math*.
@@ -97,4 +97,6 @@ for ``bytearray + bytearray`` element by element.
9797

9898
The data structure that combines a typed buffer with
9999
element-wise math is the :class:`~ulab.numpy.ndarray`.
100-
The next page opens the box.
100+
What is inside the box and how each field shapes the
101+
fast-path behaviour are the foundations the rest of
102+
this chapter rests on.

docs/openmvcam/tutorial/numpy/images.rst

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@ Images and ndarrays
44
This page closes the loop the section opened with. The
55
:class:`~image.Image` class is the fast surface for
66
camera-native pixel work: every method on it operates
7-
directly on the framebuffer in the camera's native
7+
directly on the frame buffer in the camera's native
88
pixel format. :mod:`numpy` is the generic numerical
99
surface for everything else. Two methods bridge them:
1010

11-
* :py:meth:`image.Image.to_ndarray` -- view or copy the
11+
* :meth:`image.Image.to_ndarray` -- view or copy the
1212
pixels of an image as an :class:`~ulab.numpy.ndarray`.
13-
* The :py:class:`image.Image` constructor -- build a
13+
* The :class:`image.Image` constructor -- build a
1414
fresh image from an :class:`~ulab.numpy.ndarray`.
1515

1616
Together they let an application snap a frame, hand it
@@ -21,10 +21,11 @@ back into the rest of the image library.
2121
Image to ndarray
2222
----------------
2323

24-
:py:meth:`image.Image.to_ndarray(dtype, *, buffer=None)`
25-
returns an :class:`~ulab.numpy.ndarray` whose data come
26-
from the image's pixel buffer. The output shape depends
27-
on the image format:
24+
:meth:`~image.Image.to_ndarray` returns an
25+
:class:`~ulab.numpy.ndarray` whose data come from the
26+
image's pixel buffer. The signature is
27+
``to_ndarray(dtype, *, buffer=None)``, and the output
28+
shape depends on the image format:
2829

2930
* **GRAYSCALE** -- 2-D array, shape ``(height, width)``.
3031
* **RGB565** -- 3-D array, shape ``(height, width, 3)``,
@@ -72,7 +73,7 @@ ndarray to image
7273

7374
Going the other way, pass the
7475
:class:`~ulab.numpy.ndarray` as the first argument to
75-
:py:class:`image.Image`::
76+
:class:`image.Image`::
7677

7778
image.Image(arr, *, buffer=None, copy_to_fb=False)
7879

@@ -90,7 +91,7 @@ clamped to the ``0..255`` range.
9091
``buffer=`` lets the application supply a
9192
:class:`bytearray` it already allocated for the
9293
resulting image. ``copy_to_fb=True`` writes the result
93-
into the camera's framebuffer, which is the right
94+
into the camera's frame buffer, which is the right
9495
choice when the result should appear in the IDE
9596
preview.
9697

@@ -128,7 +129,7 @@ depth, audio) in a single computation.
128129
It is **not** the right answer for high-throughput
129130
pixel processing the :class:`~image.Image` class
130131
already covers. The built-in methods operate directly
131-
on the framebuffer in the camera's native pixel format
132+
on the frame buffer in the camera's native pixel format
132133
and are much faster than the equivalent :mod:`numpy`
133134
expression. Reach for the bridge for the operations
134135
the image library does not already provide.

docs/openmvcam/tutorial/numpy/index.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,11 @@ Python ``for`` loop.
6060
signals/filtering.rst
6161

6262
.. toctree::
63-
:caption: Numerical extras
63+
:caption: Numerical tools
6464
:maxdepth: 1
6565

6666
numerical.rst
67+
solvers-and-random.rst
6768

6869
.. toctree::
6970
:caption: Images

docs/openmvcam/tutorial/numpy/linalg.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -196,11 +196,11 @@ accurate than ``np.dot(np.linalg.inv(A), b)``:
196196
x = sp.linalg.cho_solve(L, b)
197197

198198
Reach for these instead of inverting whenever the
199-
structure of ``A`` lets you -- they save elimination
199+
structure of ``A`` allows -- they save elimination
200200
work *and* the explicit inverse.
201201

202-
A worked example: small linear system
203-
-------------------------------------
202+
Solving a small linear system
203+
-----------------------------
204204

205205
::
206206

docs/openmvcam/tutorial/numpy/math/operators.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ Operators
44
The first kind of math :mod:`numpy` runs on an
55
:class:`~ulab.numpy.ndarray` is the standard Python
66
operators. Arithmetic, comparison, and bit-wise
7-
operators all work *element-wise*: each operator walks
7+
operators all work *element-wise* -- each operator walks
88
the array (or both arrays) once from start to finish
99
inside a single library call, much faster than the
1010
equivalent Python ``for`` loop.

docs/openmvcam/tutorial/numpy/math/selection.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ together::
1919
np.where(a < 3, a, 0.0)
2020
# array([1.0, 2.0, 0.0, 0.0, 0.0])
2121

22-
This is the right shape for an "if/else per element"
22+
This is the right tool for an "if/else per element"
2323
without writing a Python loop.
2424

2525
:func:`~ulab.numpy.clip(a, lo, hi)` is shorthand for
@@ -61,7 +61,7 @@ Two reductions also produce indices:
6161
a[idx] # array([10, 20, 30, 40])
6262

6363
``argsort`` always returns ``uint16``; the array
64-
being sorted must therefore have no more than 65 535
64+
being sorted must therefore have no more than 65,535
6565
elements on the sorted axis.
6666

6767
:func:`~ulab.numpy.bincount(x)` counts occurrences of each

docs/openmvcam/tutorial/numpy/math/universal-functions.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ overrides it::
143143
The Python function must take a single argument and
144144
return a single number.
145145

146-
:func:`~ulab.numpy.vectorize` is mostly *syntactic*:
146+
:func:`~ulab.numpy.vectorize` is mostly *syntactic* --
147147
the wrapped Python function still has to run once per
148148
element, so most of the per-element interpreter cost
149149
that a true ufunc avoids is back. Expect a modest

0 commit comments

Comments
 (0)