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?

3
  • 6
    Why not use TikZ? It works with any content, not just TikZ pictures. Take a look at How to create magnified subfigures and corresponding boxes for portions of a large image, where a similar application has been discussed.
    – Jake
    Jun 13, 2012 at 13:57
  • Tried that but it didn't work. In that case there probably only is a mistake in my code, so you may just as well close this question.
    – Ingo
    Jun 13, 2012 at 14:09
  • 1
    I've added an answer showing how to use the code in this case.
    – Jake
    Jun 13, 2012 at 14:31

3 Answers 3

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.

2
  • 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.
    – Kvothe
    Mar 31, 2021 at 15:47
  • Very nice! You saved me today.
    – Mikasa
    Sep 10, 2022 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}
6
  • 5
    Time for a library already, eh? :)
    – percusse
    Jun 13, 2012 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, 2012 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, 2012 at 14:47
  • 3
    How is the library coming along ;-)?
    – Ingo
    Jan 8, 2014 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 7, 2019 at 18:50
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.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .