29

Suppose I have a .png or .jpg file. Let's say this one:

Tux to be magnified

I would like to zoom in on the tip of Tux's beak. TikZ for example provides a very nice library that allows for zooming with a spyglass like style on TikZ pictures. How can I do the same for the above application?

Ingo
  • 20,035

3 Answers3

44

Also to demonstrate on a simple example:

\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{spy}
\begin{document}
\begin{tikzpicture}[spy using outlines={circle,yellow,magnification=5,size=1.5cm, connect spies}]
\node {\pgfimage[height=2cm]{tux}};
\spy on (0,0.4) in node [left] at (2,1.25);
\end{tikzpicture}
\begin{tikzpicture}[spy using outlines={circle,yellow,magnification=5,size=1.5cm, connect spies}]
\node {\pgfimage[interpolate=true,height=2cm]{tux}};
\spy on (0,0.4) in node [left] at (2,1.25);
\end{tikzpicture}
\end{document}

enter image description here

You can also use \includegraphics command too instead of \pgfimage inside the node.

percusse
  • 157,807
  • If in the end I want to have a picture that can be used in a figure environment and scaled to have a width that is some % of the \linewidth and where if I want to change that percentage I don't need to reposition the maginfiying glass locations how should I go about this? And using what coordinates do I specify the locations for the spyglass. – Marten Mar 31 '21 at 15:47
  • Very nice! You saved me today. – Mikasa Sep 10 '22 at 11:40
26

To show how the code from How to create magnified subfigures and corresponding boxes for portions of a large image would be used here:

\begin{figure}[ht]\centering
\begin{tikzpicture}[
    zoomboxarray,
    zoomboxarray columns=1,
    zoomboxarray rows=1,
    connect zoomboxes,
    zoombox paths/.append style={line width=3pt}
]
    \node [image node] { \includegraphics[width=0.45\textwidth]{tux.png} };
    \zoombox[magnification=6]{0.45,0.67}
\end{tikzpicture}
\end{figure}

will produce

If you add help grid to the image node node options, you will get

which makes it easier to position the zoom point.


Here's the complete code:

\documentclass{article}
\usepackage{graphicx}
\usepackage{caption}
\usepackage{subcaption}
\usepackage{tikz}
\usepackage{pgfplots}
\usetikzlibrary{spy,calc}
\usepackage{hyperref}


\newif\ifblackandwhitecycle
\gdef\patternnumber{0}

\pgfkeys{/tikz/.cd,
    zoombox paths/.style={
        draw=orange,
        very thick
    },
    black and white/.is choice,
    black and white/.default=static,
    black and white/static/.style={ 
        draw=white,   
        zoombox paths/.append style={
            draw=white,
            postaction={
                draw=black,
                loosely dashed
            }
        }
    },
    black and white/static/.code={
        \gdef\patternnumber{1}
    },
    black and white/cycle/.code={
        \blackandwhitecycletrue
        \gdef\patternnumber{1}
    },
    black and white pattern/.is choice,
    black and white pattern/0/.style={},
    black and white pattern/1/.style={    
            draw=white,
            postaction={
                draw=black,
                dash pattern=on 2pt off 2pt
            }
    },
    black and white pattern/2/.style={    
            draw=white,
            postaction={
                draw=black,
                dash pattern=on 4pt off 4pt
            }
    },
    black and white pattern/3/.style={    
            draw=white,
            postaction={
                draw=black,
                dash pattern=on 4pt off 4pt on 1pt off 4pt
            }
    },
    black and white pattern/4/.style={    
            draw=white,
            postaction={
                draw=black,
                dash pattern=on 4pt off 2pt on 2 pt off 2pt on 2 pt off 2pt
            }
    },
    zoomboxarray inner gap/.initial=5pt,
    zoomboxarray columns/.initial=2,
    zoomboxarray rows/.initial=2,
    subfigurename/.initial={},
    figurename/.initial={zoombox},
    zoomboxarray/.style={
        execute at begin picture={
            \begin{scope}[
                spy using outlines={%
                    zoombox paths,
                    width=\imagewidth / \pgfkeysvalueof{/tikz/zoomboxarray columns} - (\pgfkeysvalueof{/tikz/zoomboxarray columns} - 1) / \pgfkeysvalueof{/tikz/zoomboxarray columns} * \pgfkeysvalueof{/tikz/zoomboxarray inner gap} -\pgflinewidth,
                    height=\imageheight / \pgfkeysvalueof{/tikz/zoomboxarray rows} - (\pgfkeysvalueof{/tikz/zoomboxarray rows} - 1) / \pgfkeysvalueof{/tikz/zoomboxarray rows} * \pgfkeysvalueof{/tikz/zoomboxarray inner gap}-\pgflinewidth,
                    magnification=3,
                    every spy on node/.style={
                        zoombox paths
                    },
                    every spy in node/.style={
                        zoombox paths
                    }
                }
            ]
        },
        execute at end picture={
            \end{scope}
            \node at (image.north) [anchor=north,inner sep=0pt] {\subcaptionbox{\label{\pgfkeysvalueof{/tikz/figurename}-image}}{\phantomimage}};
            \node at (zoomboxes container.north) [anchor=north,inner sep=0pt] {\subcaptionbox{\label{\pgfkeysvalueof{/tikz/figurename}-zoom}}{\phantomimage}};
     \gdef\patternnumber{0}
        },
        spymargin/.initial=0.5em,
        zoomboxes xshift/.initial=1,
        zoomboxes right/.code=\pgfkeys{/tikz/zoomboxes xshift=1},
        zoomboxes left/.code=\pgfkeys{/tikz/zoomboxes xshift=-1},
        zoomboxes yshift/.initial=0,
        zoomboxes above/.code={
            \pgfkeys{/tikz/zoomboxes yshift=1},
            \pgfkeys{/tikz/zoomboxes xshift=0}
        },
        zoomboxes below/.code={
            \pgfkeys{/tikz/zoomboxes yshift=-1},
            \pgfkeys{/tikz/zoomboxes xshift=0}
        },
        caption margin/.initial=4ex,
    },
    adjust caption spacing/.code={},
    image container/.style={
        inner sep=0pt,
        at=(image.north),
        anchor=north,
        adjust caption spacing
    },
    zoomboxes container/.style={
        inner sep=0pt,
        at=(image.north),
        anchor=north,
        name=zoomboxes container,
        xshift=\pgfkeysvalueof{/tikz/zoomboxes xshift}*(\imagewidth+\pgfkeysvalueof{/tikz/spymargin}),
        yshift=\pgfkeysvalueof{/tikz/zoomboxes yshift}*(\imageheight+\pgfkeysvalueof{/tikz/spymargin}+\pgfkeysvalueof{/tikz/caption margin}),
        adjust caption spacing
    },
    calculate dimensions/.code={
        \pgfpointdiff{\pgfpointanchor{image}{south west} }{\pgfpointanchor{image}{north east} }
        \pgfgetlastxy{\imagewidth}{\imageheight}
        \global\let\imagewidth=\imagewidth
        \global\let\imageheight=\imageheight
        \gdef\columncount{1}
        \gdef\rowcount{1}
        \gdef\zoomboxcount{1}
    },
    image node/.style={
        inner sep=0pt,
        name=image,
        anchor=south west,
        append after command={
            [calculate dimensions]
            node [image container,subfigurename=\pgfkeysvalueof{/tikz/figurename}-image] {\phantomimage}
            node [zoomboxes container,subfigurename=\pgfkeysvalueof{/tikz/figurename}-zoom] {\phantomimage}
        }
    },
    color code/.style={
        zoombox paths/.append style={draw=#1}
    },
    connect zoomboxes/.style={
    spy connection path={\draw[draw=none,zoombox paths] (tikzspyonnode) -- (tikzspyinnode);}
    },
    help grid code/.code={
        \begin{scope}[
                x={(image.south east)},
                y={(image.north west)},
                font=\footnotesize,
                help lines,
                overlay
            ]
            \foreach \x in {0,1,...,9} { 
                \draw(\x/10,0) -- (\x/10,1);
                \node [anchor=north] at (\x/10,0) {0.\x};
            }
            \foreach \y in {0,1,...,9} {
                \draw(0,\y/10) -- (1,\y/10);                        \node [anchor=east] at (0,\y/10) {0.\y};
            }
        \end{scope}    
    },
    help grid/.style={
        append after command={
            [help grid code]
        }
    },
}

\newcommand\phantomimage{%
    \phantom{%
        \rule{\imagewidth}{\imageheight}%
    }%
}
\newcommand\zoombox[2][]{
    \begin{scope}[zoombox paths]
        \pgfmathsetmacro\xpos{
            (\columncount-1)*(\imagewidth / \pgfkeysvalueof{/tikz/zoomboxarray columns} + \pgfkeysvalueof{/tikz/zoomboxarray inner gap} / \pgfkeysvalueof{/tikz/zoomboxarray columns} ) + \pgflinewidth
        }
        \pgfmathsetmacro\ypos{
            (\rowcount-1)*( \imageheight / \pgfkeysvalueof{/tikz/zoomboxarray rows} + \pgfkeysvalueof{/tikz/zoomboxarray inner gap} / \pgfkeysvalueof{/tikz/zoomboxarray rows} ) + 0.5*\pgflinewidth
        }
        \edef\dospy{\noexpand\spy [
            #1,
            zoombox paths/.append style={
                black and white pattern=\patternnumber
            },
            every spy on node/.append style={#1},
            x=\imagewidth,
            y=\imageheight
        ] on (#2) in node [anchor=north west] at ($(zoomboxes container.north west)+(\xpos pt,-\ypos pt)$);}
        \dospy
        \pgfmathtruncatemacro\pgfmathresult{ifthenelse(\columncount==\pgfkeysvalueof{/tikz/zoomboxarray columns},\rowcount+1,\rowcount)}
        \global\let\rowcount=\pgfmathresult
        \pgfmathtruncatemacro\pgfmathresult{ifthenelse(\columncount==\pgfkeysvalueof{/tikz/zoomboxarray columns},1,\columncount+1)}
        \global\let\columncount=\pgfmathresult
        \ifblackandwhitecycle
            \pgfmathtruncatemacro{\newpatternnumber}{\patternnumber+1}
            \global\edef\patternnumber{\newpatternnumber}
        \fi
    \end{scope}
}


\begin{document}

\begin{figure}[ht]\centering
\begin{tikzpicture}[
    zoomboxarray,
    zoomboxarray columns=1,
    zoomboxarray rows=1,
    connect zoomboxes,
    zoombox paths/.append style={line width=3pt}
]
    \node [image node] { \includegraphics[width=0.45\textwidth]{tux.png} };
    \zoombox[magnification=6]{0.45,0.67}
\end{tikzpicture}
\end{figure}

\end{document}
Jake
  • 232,450
  • 5
    Time for a library already, eh? :) – percusse Jun 13 '12 at 14:43
  • 4
    @percusse: Hehe, yes, it definitely is. I'm starting a PhD in a couple of weeks - judging from Martin Scharrer's example, that's the time to write packages. – Jake Jun 13 '12 at 14:44
  • 1
    Hahaha, so true. I have mixed feelings. Wow,congrats! and oh no, why? at the same time. All the best! – percusse Jun 13 '12 at 14:47
  • 3
    How is the library coming along ;-)? – Ingo Jan 08 '14 at 11:02
  • 1
    In my presentations, I like to use spy to zoom into tiny parts of a complex image. The zoombox is usually adjacent to the original image since slides have sufficient horizontal space. Using TikZ and spy is quite tedious, specially getting things to align correctly, and changing magnification throws everything off. So a package based on this answer to address the common use case I just described would be awesome. – jsb Nov 07 '19 at 18:50
  • Thx. Good to know the codes! – Mikasa Sep 10 '22 at 11:41
6

You can "zoom out" some part of an image by clipping the other material away and then scale the remainder:

\documentclass{article}
\usepackage{graphicx}
\begin{document}

\includegraphics[clip,viewport=85 195 160 240,width=4cm]{tux}

\end{document}

Here viewport is used to specify the lower-left and upper-right corner of the area (in bp by default). You can also use the trim key to trim material from the left, bottom, right and top away. You need to figure out the values by trial and error or measure them. The scaling can be done using width, height or scale.

Also, the adjustbox package and its additional keys can be helpful here. Then you can use e.g. .4\width to clip away 40% of the left etc.

Martin Scharrer
  • 262,582