{"id":427,"date":"2026-08-05T19:51:10","date_gmt":"2026-08-05T19:51:10","guid":{"rendered":"https:\/\/summergeometry.org\/sgi2026\/?p=427"},"modified":"2026-08-05T19:51:12","modified_gmt":"2026-08-05T19:51:12","slug":"self-rectifying-textures","status":"publish","type":"post","link":"https:\/\/summergeometry.org\/sgi2026\/self-rectifying-textures\/","title":{"rendered":"Self-Rectifying Textures"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">By: Kyle Loh, Mahlet Girma, Max Dunitz<\/p>\n\n\n\n<p class=\"alignwide wp-block-paragraph\">If you have ever had to flatten out a bag of chips so the checkout scanner would finally read it, you already understand the problem we worked on this week. A code printed on a surface that bends, folds, or curves is hard to read, and QR codes and barcodes carry one disadvantage here: they are obvious. In supply-chain tracking, that single visible code can be tampered with, causing a product to be moved into a market it was not meant for. Our project examines self-rectifying textures, which are patterns that look like random noise, but their autocorrelation contains a regular lattice. By measuring how that lattice bends in a photo, we can recover how the surface was deformed, without printing a single visible marker.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"734\" height=\"446\" src=\"https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/opening_image-1.png\" alt=\"\" class=\"wp-image-443\" srcset=\"https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/opening_image-1.png 734w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/opening_image-1-300x182.png 300w\" sizes=\"auto, (max-width: 734px) 100vw, 734px\" \/><figcaption class=\"wp-element-caption\">A self-rectifying texture and a QR code printed on paper folded across an edge. The fold makes the QR code unreadable, but the texture reader still recovers the deformation and decodes the tracking information (Bencheikh et al., WACV 2026).<\/figcaption><\/figure>\n\n\n\n<p class=\"alignwide wp-block-paragraph\">The usual way to undo a perspective distortion is to find distinct points (for example, the three eyes of a QR code) and use their pixel positions to solve for a homography. To stay hidden, self-rectifying textures move the landmarks into the autocorrelation of the image. <br><br><strong>Definition of Autocorrelation:<\/strong> The autocorrelation measures how much an image resembles a shifted copy of itself. It is basically a dot product. For a 2D image <math><semantics><mrow><mi>f<\/mi><mo lspace=\"0.2222em\" rspace=\"0.2222em\">:<\/mo><msup><mi>\u211d<\/mi><mn>2<\/mn><\/msup><mo stretchy=\"false\">\u2192<\/mo><mi>\u211d<\/mi><\/mrow><annotation encoding=\"application\/x-tex\">f: \\mathbb{R}^2 \\rightarrow \\mathbb{R}<\/annotation><\/semantics><\/math>, the autocorrelation is defined as follows:<\/p>\n\n\n\n<div class=\"wp-block-math\"><math display=\"block\"><semantics><mrow><msub><mi>R<\/mi><mrow><mi>f<\/mi><mo separator=\"true\">,<\/mo><mi>f<\/mi><\/mrow><\/msub><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mi>\u03c4<\/mi><mo form=\"postfix\" stretchy=\"false\">)<\/mo><mo>=<\/mo><msub><mo>\u222b<\/mo><msup><mi>\u211d<\/mi><mn>2<\/mn><\/msup><\/msub><mi>f<\/mi><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mi>x<\/mi><mo form=\"postfix\" stretchy=\"false\">)<\/mo><mspace width=\"0.1667em\"><\/mspace><mi>f<\/mi><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mi>x<\/mi><mo>+<\/mo><mi>\u03c4<\/mi><mo form=\"postfix\" stretchy=\"false\">)<\/mo><mspace width=\"0.1667em\"><\/mspace><mi>d<\/mi><mi>x<\/mi><\/mrow><annotation encoding=\"application\/x-tex\"> R_{f,f}(\\tau) = \\int_{\\mathbb{R}^2} f(x)\\, f(x + \\tau)\\, dx<\/annotation><\/semantics><\/math><\/div>\n\n\n\n<p class=\"alignwide wp-block-paragraph\">where <math><semantics><mi>x<\/mi><annotation encoding=\"application\/x-tex\">x<\/annotation><\/semantics><\/math> is a pixel position and the lag (or shift) <math><semantics><mrow><mi>\u03c4<\/mi><mo>\u2208<\/mo><msup><mi>\u211d<\/mi><mn>2<\/mn><\/msup><\/mrow><annotation encoding=\"application\/x-tex\">\\tau \\in \\mathbb{R}^2<\/annotation><\/semantics><\/math>. A <strong>peak <\/strong>is defined as the <math><semantics><mi>\u03c4<\/mi><annotation encoding=\"application\/x-tex\">\\tau<\/annotation><\/semantics><\/math> where <math><semantics><mrow><msub><mi>R<\/mi><mrow><mi>f<\/mi><mo separator=\"true\">,<\/mo><mi>f<\/mi><\/mrow><\/msub><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mi>\u03c4<\/mi><mo form=\"postfix\" stretchy=\"false\">)<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">R_{f,f}(\\tau)<\/annotation><\/semantics><\/math> attains a local maximum. This operation is translation-invariant. <br><br>As autocorrelations are computationally expensive <math><semantics><mrow><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mi class=\"mathcal\">\ud835\udcaa<\/mi><mo form=\"prefix\" stretchy=\"false\">(<\/mo><msup><mi>N<\/mi><mn>2<\/mn><\/msup><mo form=\"postfix\" stretchy=\"false\">)<\/mo><mo form=\"postfix\" stretchy=\"false\">)<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">(\\mathcal{O}(N^2))<\/annotation><\/semantics><\/math>, we never compute them directly. Instead, we invoke the Wiener-Khinchin theorem, allowing us to compute the autocorrelation in terms of Fourier transforms (<math><semantics><mi class=\"mathcal\">\u2131<\/mi><annotation encoding=\"application\/x-tex\">\\mathcal{F}<\/annotation><\/semantics><\/math>): <math><semantics><mrow><mi>R<\/mi><mo>=<\/mo><msup><mi class=\"mathcal\">\u2131<\/mi><mrow><mo lspace=\"0em\" rspace=\"0em\">\u2212<\/mo><mn>1<\/mn><\/mrow><\/msup><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mi>|<\/mi><mi class=\"mathcal\">\u2131<\/mi><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mi>f<\/mi><mo form=\"postfix\" stretchy=\"false\">)<\/mo><msup><mi>|<\/mi><mn>2<\/mn><\/msup><mo form=\"postfix\" stretchy=\"false\">)<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">R = \\mathcal{F}^{-1}(|\\mathcal{F}(f)|^2)<\/annotation><\/semantics><\/math> in <math><semantics><mrow><mi class=\"mathcal\">\ud835\udcaa<\/mi><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mi>N<\/mi><mrow><mspace width=\"0.1667em\"><\/mspace><mi>log<\/mi><mo>\u2061<\/mo><mspace width=\"0.1667em\"><\/mspace><\/mrow><mi>N<\/mi><mo form=\"postfix\" stretchy=\"false\">)<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">\\mathcal{O}(N\\log N)<\/annotation><\/semantics><\/math>.<br><br><strong>Task 1 &#8211; Constructing Textures: <\/strong>We used a grayscale-version of Steamboat Willie with a fronto-parallel view as our <strong>base image<\/strong>.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"506\" src=\"https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/base_willie-1-1024x506.jpeg\" alt=\"\" class=\"wp-image-444\" srcset=\"https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/base_willie-1-1024x506.jpeg 1024w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/base_willie-1-300x148.jpeg 300w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/base_willie-1-768x379.jpeg 768w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/base_willie-1.jpeg 1192w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">The base image (left) and its autocorrelation plot (right) with unshifted copy <math><semantics><mrow><mi>\u03c4<\/mi><mo>=<\/mo><mn>0.<\/mn><\/mrow><annotation encoding=\"application\/x-tex\">\\tau=0.<\/annotation><\/semantics><\/math> A plain image with no modifications give one peak at the center corresponding to <math><semantics><mrow><mi>\u03c4<\/mi><mo>=<\/mo><mn>0<\/mn><\/mrow><annotation encoding=\"application\/x-tex\">\\tau=0<\/annotation><\/semantics><\/math>.<\/figcaption><\/figure>\n\n\n\n<p class=\"alignwide wp-block-paragraph\">Next, we superimposed three copies of the image, each offset by shift vectors <math><semantics><mrow><mn>0<\/mn><mo separator=\"true\">,<\/mo><mo form=\"prefix\" stretchy=\"false\">+<\/mo><mi>u<\/mi><mo separator=\"true\">,<\/mo><mo form=\"prefix\" stretchy=\"false\">+<\/mo><mi>v<\/mi><mo>\u2208<\/mo><msup><mi>\u211d<\/mi><mn>2<\/mn><\/msup><\/mrow><annotation encoding=\"application\/x-tex\">0, +u, +v \\in \\mathbb{R}^2<\/annotation><\/semantics><\/math>, using zero-padding. We call the superimposed image the <strong>base texture<\/strong>. The following autocorrelation plot would have six peaks, at <math><semantics><mrow><mo form=\"prefix\" stretchy=\"false\">{<\/mo><mo form=\"prefix\" stretchy=\"false\">\u00b1<\/mo><mi>u<\/mi><mo separator=\"true\">,<\/mo><mo form=\"prefix\" stretchy=\"false\">\u00b1<\/mo><mi>v<\/mi><mo separator=\"true\">,<\/mo><mo form=\"prefix\" stretchy=\"false\">\u00b1<\/mo><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mi>u<\/mi><mo>\u2212<\/mo><mi>v<\/mi><mo form=\"postfix\" stretchy=\"false\">)<\/mo><mo form=\"postfix\" stretchy=\"false\">}<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">\\{ \\pm u, \\pm v, \\pm (u-v)\\} <\/annotation><\/semantics><\/math>. This is known as the <strong>Fundamental Hexagon<\/strong>. Then, we apply a deformation <math><semantics><mi>A<\/mi><annotation encoding=\"application\/x-tex\">A<\/annotation><\/semantics><\/math> to the superimposed image, causing the fundamental hexagon to become <math><semantics><mrow><mo form=\"prefix\" stretchy=\"false\">{<\/mo><mo form=\"prefix\" stretchy=\"false\">\u00b1<\/mo><mi>A<\/mi><mi>u<\/mi><mo separator=\"true\">,<\/mo><mo form=\"prefix\" stretchy=\"false\">\u00b1<\/mo><mi>A<\/mi><mi>v<\/mi><mo separator=\"true\">,<\/mo><mo form=\"prefix\" stretchy=\"false\">\u00b1<\/mo><mi>A<\/mi><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mi>u<\/mi><mo>\u2212<\/mo><mi>v<\/mi><mo form=\"postfix\" stretchy=\"false\">)<\/mo><mo form=\"postfix\" stretchy=\"false\">}<\/mo><mi>.<\/mi><\/mrow><annotation encoding=\"application\/x-tex\">\\{ \\pm Au, \\pm Av, \\pm A(u-v)\\}.<\/annotation><\/semantics><\/math> <\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"506\" src=\"https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/superimposed_willie-2-1024x506.jpeg\" alt=\"\" class=\"wp-image-448\" srcset=\"https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/superimposed_willie-2-1024x506.jpeg 1024w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/superimposed_willie-2-300x148.jpeg 300w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/superimposed_willie-2-768x379.jpeg 768w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/superimposed_willie-2.jpeg 1192w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">The superimposed texture (left) and its six-peak fundamental hexagon (right).<\/figcaption><\/figure>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"506\" src=\"https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/deformed_willie-1-1024x506.jpeg\" alt=\"\" class=\"wp-image-446\" srcset=\"https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/deformed_willie-1-1024x506.jpeg 1024w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/deformed_willie-1-300x148.jpeg 300w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/deformed_willie-1-768x379.jpeg 768w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/deformed_willie-1.jpeg 1192w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">The deformed texture (left) and its warped hexagon (right).<\/figcaption><\/figure>\n\n\n\n<p class=\"alignwide wp-block-paragraph\"><strong>The Assignment Problem<\/strong>: Suppose you know the original shifts  <math><semantics><mrow><mo form=\"prefix\" stretchy=\"false\">{<\/mo><mo form=\"prefix\" stretchy=\"false\">\u00b1<\/mo><mi>u<\/mi><mo separator=\"true\">,<\/mo><mo form=\"prefix\" stretchy=\"false\">\u00b1<\/mo><mi>v<\/mi><mo separator=\"true\">,<\/mo><mo form=\"prefix\" stretchy=\"false\">\u00b1<\/mo><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mi>u<\/mi><mo>\u2212<\/mo><mi>v<\/mi><mo form=\"postfix\" stretchy=\"false\">)<\/mo><mo form=\"postfix\" stretchy=\"false\">}<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">\\{ \\pm u, \\pm v, \\pm (u-v)\\} <\/annotation><\/semantics><\/math> and have access to the base texture, but not the deformation. Unfortunately, the deformed hexagon <math><semantics><mrow><mo form=\"prefix\" stretchy=\"false\">{<\/mo><mo form=\"prefix\" stretchy=\"false\">\u00b1<\/mo><mi>A<\/mi><mi>u<\/mi><mo separator=\"true\">,<\/mo><mo form=\"prefix\" stretchy=\"false\">\u00b1<\/mo><mi>A<\/mi><mi>v<\/mi><mo separator=\"true\">,<\/mo><mo form=\"prefix\" stretchy=\"false\">\u00b1<\/mo><mi>A<\/mi><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mi>u<\/mi><mo>\u2212<\/mo><mi>v<\/mi><mo form=\"postfix\" stretchy=\"false\">)<\/mo><mo form=\"postfix\" stretchy=\"false\">}<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">\\{ \\pm Au, \\pm Av, \\pm A(u-v)\\}<\/annotation><\/semantics><\/math> alone does not tell you which peak corresponds to <math><semantics><mrow><mi>A<\/mi><mi>u<\/mi><\/mrow><annotation encoding=\"application\/x-tex\">Au<\/annotation><\/semantics><\/math> and which to <math><semantics><mrow><mi>A<\/mi><mi>v<\/mi><\/mrow><annotation encoding=\"application\/x-tex\">Av<\/annotation><\/semantics><\/math>, or their signs. However, if you can figure out this assignment problem, then you can immediately solve for <math><semantics><mi>A<\/mi><annotation encoding=\"application\/x-tex\">A<\/annotation><\/semantics><\/math> with some linear algebra. <br><br>Given the symmetries, there are eight sign-and-order combinations. Using certain invariance properties, this can be reduced to six combinations. We find and apply six candidate inverse maps <math><semantics><msup><mi>A<\/mi><mrow><mo lspace=\"0em\" rspace=\"0em\">\u2212<\/mo><mn>1<\/mn><\/mrow><\/msup><annotation encoding=\"application\/x-tex\">A^{-1}<\/annotation><\/semantics><\/math> and select the inverse map with the highest <strong>normalized cross-correlation<\/strong> score against the base texture. This is a computationally expensive solution, and for future work, we will explore more efficient methods for the assignment problem.<br><br><strong>Task 2 &#8211; Full Rectification Pipeline:<\/strong> We introduce a method that no longer assumes a uniform linear deformation. Our goal is to recover a global inverse map <math><semantics><msup><mi>\u03d5<\/mi><mrow><mo lspace=\"0em\" rspace=\"0em\">\u2212<\/mo><mn>1<\/mn><\/mrow><\/msup><annotation encoding=\"application\/x-tex\">\\phi^{-1}<\/annotation><\/semantics><\/math> that maps the observed texture back to a fronto-parallel image.<br>For this experiment, we used blurred Gaussian white noise as our base image, constructing a texture by superimposing it with two shifted copies of itself and zero-padding. We then apply a homography deformation <math><semantics><mi>A<\/mi><annotation encoding=\"application\/x-tex\">A<\/annotation><\/semantics><\/math>.<\/p>\n\n\n\n<figure class=\"wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-1 is-layout-flex wp-block-gallery-is-layout-flex\">\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"532\" data-id=\"451\" src=\"https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-02-at-5.17.05-PM-1024x532.png\" alt=\"\" class=\"wp-image-451\" srcset=\"https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-02-at-5.17.05-PM-1024x532.png 1024w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-02-at-5.17.05-PM-300x156.png 300w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-02-at-5.17.05-PM-768x399.png 768w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-02-at-5.17.05-PM-1200x624.png 1200w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-02-at-5.17.05-PM.png 1320w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n<\/figure>\n\n\n\n<p class=\"alignwide wp-block-paragraph\"><strong>Methodology: <\/strong>We sample small square patches from the grid. Then, we compute each patch&#8217;s autocorrelation and patches with &#8220;unreliable measurements&#8221; are discarded &#8212; criteria include: no valid fundamental hexagon detected or the inverse Jacobian&#8217;s determinant is a statistical outlier). For each patch, we detect the warped fundamental hexagon in each patch&#8217;s autocorrelation. Comparing these peaks with the known original shifts provides six candidates for the local inverse Jacobian <math><semantics><mrow><mi>D<\/mi><msup><mi>\u03d5<\/mi><mrow><mo lspace=\"0em\" rspace=\"0em\">\u2212<\/mo><mn>1<\/mn><\/mrow><\/msup><mo form=\"prefix\" stretchy=\"false\">(<\/mo><msub><mi>y<\/mi><mi>i<\/mi><\/msub><mo form=\"postfix\" stretchy=\"false\">)<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">D\\phi^{-1}(y_i)<\/annotation><\/semantics><\/math>.<br><br>Then, we construct a Delaunay triangulation mesh of the center points of the remaining patches. Two measurements are treated as neighbors when their centers share an edge in the mesh. To select on candidate Jacobian at each vertex, we assume that the physical deformation varies smoothly, so the correct matrices at neighboring vertices should be similar.<br><br>We resolve the candidate ambiguity using <strong>Minimum Spanning Tree propagation<\/strong>. First, we use a phase correlation on one patch with the original template to select one initial vertex and its actual local Jacobian <math><semantics><mrow><mi>D<\/mi><msup><mi>\u03d5<\/mi><mrow><mo lspace=\"0em\" rspace=\"0em\">\u2212<\/mo><mn>1<\/mn><\/mrow><\/msup><mo form=\"prefix\" stretchy=\"false\">(<\/mo><msub><mi>y<\/mi><mi>i<\/mi><\/msub><mo form=\"postfix\" stretchy=\"false\">)<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">D\\phi^{-1}(y_i)<\/annotation><\/semantics><\/math>. Suppose vertex <math><semantics><mi>i<\/mi><annotation encoding=\"application\/x-tex\">i<\/annotation><\/semantics><\/math> has been already assigned the matrix <math><semantics><msubsup><mi>G<\/mi><mi>i<\/mi><mo>\u2217<\/mo><\/msubsup><annotation encoding=\"application\/x-tex\">G_i^\\ast<\/annotation><\/semantics><\/math> while an adjacent vertex <math><semantics><mi>j<\/mi><annotation encoding=\"application\/x-tex\">j<\/annotation><\/semantics><\/math> remains unassigned. For each of the six candidates at <math><semantics><mi>j<\/mi><annotation encoding=\"application\/x-tex\">j<\/annotation><\/semantics><\/math>, we compute the mismatch<\/p>\n\n\n\n<div class=\"wp-block-math\"><math display=\"block\"><semantics><mrow><msubsup><mi>d<\/mi><mrow><mi>i<\/mi><mi>j<\/mi><\/mrow><mrow><mo form=\"prefix\" stretchy=\"false\" lspace=\"0em\" rspace=\"0em\">(<\/mo><mi>k<\/mi><mo form=\"postfix\" stretchy=\"false\" lspace=\"0em\" rspace=\"0em\">)<\/mo><\/mrow><\/msubsup><mo>=<\/mo><mi>\u2016<\/mi><msubsup><mi>G<\/mi><mi>i<\/mi><mo>\u2217<\/mo><\/msubsup><mo>\u2212<\/mo><msubsup><mi>G<\/mi><mi>j<\/mi><mrow><mo form=\"prefix\" stretchy=\"false\" lspace=\"0em\" rspace=\"0em\">(<\/mo><mi>k<\/mi><mo form=\"postfix\" stretchy=\"false\" lspace=\"0em\" rspace=\"0em\">)<\/mo><\/mrow><\/msubsup><msub><mi>\u2016<\/mi><mi>F<\/mi><\/msub><\/mrow><annotation encoding=\"application\/x-tex\">d_{ij}^{(k)} = \\| G_i^\\ast &#8211; G_j^{(k)} \\|_F<\/annotation><\/semantics><\/math><\/div>\n\n\n\n<p class=\"alignwide wp-block-paragraph\">and take the candidate with the minimum Frobenius norm. All edges from assigned vertices to unassigned vertices are stored in a priority queue, and we iteratively assign matrices to unassigned vertices adjacent to vertices with assignment.  <br>Finally, we use a finite-element method that reconstructs a global inverse map whose gradient best matches the selected local Jacobians:<\/p>\n\n\n\n<div class=\"wp-block-math\"><math display=\"block\"><semantics><mrow><msup><mover><mi>\u03d5<\/mi><mo stretchy=\"false\">^<\/mo><\/mover><mrow><mo lspace=\"0em\" rspace=\"0em\">\u2212<\/mo><mn>1<\/mn><\/mrow><\/msup><mo>=<\/mo><munder><mi>arg\u2009min<\/mi><mrow><mi>\u03c8<\/mi><mo>\u2208<\/mo><msub><mi>V<\/mi><mi>h<\/mi><\/msub><\/mrow><\/munder><mo>\u2061<\/mo><mspace width=\"0.1667em\"><\/mspace><msub><mo>\u222b<\/mo><mrow><mi mathvariant=\"normal\">\u03a9<\/mi><\/mrow><\/msub><mi>\u2016<\/mi><mo>\u2207<\/mo><mi>\u03c8<\/mi><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mi>y<\/mi><mo form=\"postfix\" stretchy=\"false\">)<\/mo><mo>\u2212<\/mo><msup><mi>A<\/mi><mrow><mo lspace=\"0em\" rspace=\"0em\">\u2212<\/mo><mn>1<\/mn><\/mrow><\/msup><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mi>y<\/mi><mo form=\"postfix\" stretchy=\"false\">)<\/mo><msub><mi>\u2016<\/mi><mi>F<\/mi><\/msub><mi>d<\/mi><mi>y<\/mi><mi>.<\/mi><\/mrow><annotation encoding=\"application\/x-tex\">\\hat{\\phi}^{-1} = \\argmin_{\\psi \\in V_h} \\int_\\Omega \\| \\nabla \\psi(y)- A^{-1}(y)\\|_F  dy.<\/annotation><\/semantics><\/math><\/div>\n\n\n\n<p class=\"alignwide wp-block-paragraph\">This determines the map up to a constant translation (the +C in integration), so we anchor the reconstructed mesh to the image boundary. We then regrid the deformed pixel values through the recovered map to produce a rectified image. For validation, we compare this result with the original template using the absolute error <math><semantics><mrow><mi>|<\/mi><mi>r<\/mi><mi>e<\/mi><mi>c<\/mi><mi>t<\/mi><mi>i<\/mi><mi>f<\/mi><mi>i<\/mi><mi>e<\/mi><mi>d<\/mi><mo>\u2212<\/mo><mi>t<\/mi><mi>e<\/mi><mi>m<\/mi><mi>p<\/mi><mi>l<\/mi><mi>a<\/mi><mi>t<\/mi><mi>e<\/mi><mi>|<\/mi><\/mrow><annotation encoding=\"application\/x-tex\">|rectified &#8211; template|<\/annotation><\/semantics><\/math>. <\/p>\n\n\n\n<figure class=\"wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-2 is-layout-flex wp-block-gallery-is-layout-flex\">\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"480\" data-id=\"452\" src=\"https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-02-at-5.17.32-PM-1024x480.png\" alt=\"\" class=\"wp-image-452\" srcset=\"https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-02-at-5.17.32-PM-1024x480.png 1024w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-02-at-5.17.32-PM-300x141.png 300w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-02-at-5.17.32-PM-768x360.png 768w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-02-at-5.17.32-PM-1200x563.png 1200w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-02-at-5.17.32-PM.png 1318w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n<figcaption class=\"blocks-gallery-caption wp-element-caption\">Rectified, regridded result (left) and the absolute error against template (right).<\/figcaption><\/figure>\n\n\n\n<p class=\"alignwide wp-block-paragraph\"><strong>Task 3 &#8211; Non-Planar Texture Rectification via Cylindrical Mapping:<\/strong> We extend our rectification pipeline to non-planar surfaces (particularly, a homography-deformed white-noise texture wrapped around a cylinder) utilizing the same MST and Finite Element Method pipeline from Task 2 to recover a global inverse deformation map.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"970\" height=\"520\" src=\"https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-03-at-9.45.42-PM.png\" alt=\"\" class=\"wp-image-476\" srcset=\"https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-03-at-9.45.42-PM.png 970w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-03-at-9.45.42-PM-300x161.png 300w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-03-at-9.45.42-PM-768x412.png 768w\" sizes=\"auto, (max-width: 970px) 100vw, 970px\" \/><figcaption class=\"wp-element-caption\">Flattened base texture (left) and cylindrically warped base texture (right).<\/figcaption><\/figure>\n\n\n\n<p class=\"alignwide wp-block-paragraph\"><strong>Note<\/strong>: To model deformations on cylindrical geometry, spatial coordinates <math><semantics><mrow><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mi>X<\/mi><mo separator=\"true\">,<\/mo><mi>Y<\/mi><mo form=\"postfix\" stretchy=\"false\">)<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">(X,Y)<\/annotation><\/semantics><\/math> in the planar texture domain are mapped to 3D surface coordinates <math><semantics><mrow><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mi>X<\/mi><mo separator=\"true\">,<\/mo><mi>Y<\/mi><mo separator=\"true\">,<\/mo><mi>Z<\/mi><mo form=\"postfix\" stretchy=\"false\">)<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">(X,Y, Z)<\/annotation><\/semantics><\/math> for a cylinder of radius R=260px. To unroll or warp texture fields back into the reference domain, we map the angular coordinates <math><semantics><mrow><mi>\u03b8<\/mi><mo>=<\/mo><mtext>atan2<\/mtext><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mi>Y<\/mi><mo separator=\"true\">,<\/mo><mi>X<\/mi><mo form=\"postfix\" stretchy=\"false\">)<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">\\theta = \\text{atan2}(Y,X)<\/annotation><\/semantics><\/math> back into 2D planar coordinates <math><semantics><mrow><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mi>X<\/mi><mo separator=\"true\">,<\/mo><mi>Y<\/mi><mo form=\"postfix\" stretchy=\"false\">)<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">(X,Y)<\/annotation><\/semantics><\/math>.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"895\" height=\"516\" src=\"https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-03-at-9.47.36-PM.png\" alt=\"\" class=\"wp-image-477\" style=\"aspect-ratio:1.7345714666548184\" srcset=\"https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-03-at-9.47.36-PM.png 895w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-03-at-9.47.36-PM-300x173.png 300w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-03-at-9.47.36-PM-768x443.png 768w\" sizes=\"auto, (max-width: 895px) 100vw, 895px\" \/><figcaption class=\"wp-element-caption\">Flattened deformed texture (left) and cylindrically warped deformed texture (right)<\/figcaption><\/figure>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"386\" src=\"https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-03-at-9.49.56-PM-1024x386.png\" alt=\"\" class=\"wp-image-478\" srcset=\"https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-03-at-9.49.56-PM-1024x386.png 1024w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-03-at-9.49.56-PM-300x113.png 300w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-03-at-9.49.56-PM-768x289.png 768w, https:\/\/summergeometry.org\/sgi2026\/wp-content\/uploads\/2026\/08\/Screenshot-2026-08-03-at-9.49.56-PM.png 1163w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">The resultant unrolled rectified texture (left), cylindrically warped rectified texture (center), and the absolute error against template (right).<\/figcaption><\/figure>\n\n\n\n<p class=\"alignwide wp-block-paragraph\"><strong>Conclusion: <\/strong>Our rectification pipeline has a clear strength: most internal vertices (from the Delaunay triangulation) have sub-pixel error in the rectification and are accurate intensity-wise. However, the pipeline also has two clear weaknesses: (1) there are larger intensity errors at the edges and corners, most likely from our failure to collect as many &#8220;good&#8221; observations near the edges and corners of the texture and (2) the dependency on accessing the template at least once in the process. Future work would entail resolving these two issues. <br><br><strong>References: <\/strong><br>[1] Bencheikh, Ismail, et al. &#8220;Autocorrelation-based Fiducial Markers for Traceability.&#8221;&nbsp;<em>Proceedings of the IEEE\/CVF Winter Conference on Applications of Computer Vision<\/em>. 2026.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>By: Kyle Loh, Mahlet Girma, Max Dunitz If you have ever had to flatten out a bag of chips so the checkout scanner would finally read it, you already understand the problem we worked on this week. A code printed on a surface that bends, folds, or curves is hard to read, and QR codes [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[110],"tags":[],"ppma_author":[31,39],"class_list":["post-427","post","type-post","status-publish","format-standard","hentry","category-research"],"authors":[{"term_id":31,"user_id":0,"is_guest":1,"slug":"cap-kyledloh","display_name":"kyledloh","avatar_url":"https:\/\/secure.gravatar.com\/avatar\/?s=96&d=mm&r=g","author_category":"","first_name":"","last_name":"","user_url":"","job_title":"","description":""},{"term_id":39,"user_id":0,"is_guest":1,"slug":"cap-mahletassefa07","display_name":"mahletassefa07","avatar_url":"https:\/\/secure.gravatar.com\/avatar\/?s=96&d=mm&r=g","author_category":"","first_name":"","last_name":"","user_url":"","job_title":"","description":""}],"_links":{"self":[{"href":"https:\/\/summergeometry.org\/sgi2026\/wp-json\/wp\/v2\/posts\/427","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/summergeometry.org\/sgi2026\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/summergeometry.org\/sgi2026\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/summergeometry.org\/sgi2026\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/summergeometry.org\/sgi2026\/wp-json\/wp\/v2\/comments?post=427"}],"version-history":[{"count":10,"href":"https:\/\/summergeometry.org\/sgi2026\/wp-json\/wp\/v2\/posts\/427\/revisions"}],"predecessor-version":[{"id":492,"href":"https:\/\/summergeometry.org\/sgi2026\/wp-json\/wp\/v2\/posts\/427\/revisions\/492"}],"wp:attachment":[{"href":"https:\/\/summergeometry.org\/sgi2026\/wp-json\/wp\/v2\/media?parent=427"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/summergeometry.org\/sgi2026\/wp-json\/wp\/v2\/categories?post=427"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/summergeometry.org\/sgi2026\/wp-json\/wp\/v2\/tags?post=427"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/summergeometry.org\/sgi2026\/wp-json\/wp\/v2\/ppma_author?post=427"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}