Maurycy
Maurycy

Reputation: 1322

How to set border color to the edge pixel in ImageMagick

In order to prevent artifacts when scaling sprites or displaying them on not full pixels in my graphics engine of choice I have a need to go through all my sprites and add an edge to each one of them. The edge needs to be of the color of the neighboring pixels, just like this:

Expected effect

The left image is the original image and the right one is the expected result.

Is it possible to achieve this effect in ImageMagick? In other words, can you expand the image in all four directions by one pixel, copying the edge pixels into the newly added border?

Upvotes: 3

Views: 2014

Answers (3)

Arjan Frans
Arjan Frans

Reputation: 491

Using Virtual Pixels:

convert -resize "%[fx:w+4]x%[fx:h+4]" "./input.png" -set option:distort:viewport "%[fx:w+4]x%[fx:h+4]"-2-2 -virtual-pixel Edge -distort SRT 0 +repage ./output.png

input.png is the input image. output.png will have it's border extended by 2 pixels with the colors of the original edge. The image is resized by adding twice the extended width. The viewport offset is set to extended width.

See this page for more information.

Upvotes: 0

Mark Setchell
Mark Setchell

Reputation: 207345

I think the easiest way to tackle this is to resize the original image to be 2 pixels taller and 2 pixels wider than the original, and then to avoid distortions in the interior pixels, composite the unchanged original picture back on top in the centre.

So, to get the dimensions of the output image, by adding 2 to width and height,

geom=$(identify -format "%[fx:w+2]x%[fx:h+2]" sprite.png)

that gives something like

34x34

if the original image is 32x32.

Then I resize the image to this new size to fill the border pixels and composite the unchanged original back on top

convert sprite.png -resize $geom -gravity center sprite.png -composite out.png

enter image description here

Upvotes: 1

Kurt Pfeifle
Kurt Pfeifle

Reputation: 90193

First, you should be aware about the following basic image operations, which will serve as building blocks to the full command:

  1. image.ext[NxM+x+y] will cut out an NxM pixels-sized area from image.ext that is offset by +x pixels to the right and +y pixels to the bottom, calculated from the top left corner.
  2. +append appends two (or more) images horizontally.
  3. -append appends two (or more) images vertically.
  4. image.ext[1x+0+0] cuts off the left-most column of pixels in an image.
  5. -flop will mirror an image from left to right.
  6. -flip will mirror an image from top to bottom.
  7. image.ext[x1+0+0] cuts out the top-most row of pixels in an image.
  8. image.ext[1x+0+0] cuts out the left-most column of pixels in an image.
  9. -crop NxM+x+y does the same as 1..
  10. mpr:ABC will write an image into a Magick Persistent Registry, using the label ABC. From an MPR, which resides in a special memory location, ImageMagick can retrieve a copy of the image simply by invoking the label name.
  11. -delete 0 will delete the image with index 0 from the current image sequence.
  12. +write will write the image sequence under the name given.
  13. +repage normalizes the image canvas again.

Now you can combine 5. with 8. to cut out the right-most row of pixels:

-flop image.ext[1x+0+0]

You can also combine 6. with 7. to cut out the bottom-most column of pixels:

-flip image.ext[x1+0+0]

Now combine these effects with the parentheses syntax \( ... \) for side-processing of images for the complete command:

convert image.png                     \
  -respect-parentheses                \
  +write mpr:ABC                      \
  -delete 0                           \
    \( mpr:ABC[1x+0+0] \)             \
    \( mpr:ABC \)                     \
    \( mpr:ABC -flop -crop 1x+0+0 \)  \
  +append +repage                     \
  +write mpr:XYZ                      \
  -delete 0                           \
    \( mpr:XYZ[x1+0+0] \)             \
    \( mpr:XYZ \)                     \
    \( mpr:XYZ -flip -crop x1+0+0 \)  \
  -append +repage                     \
  extended-edges.png

Applying this to the builtin rose: image:

convert rose:                         \
  -respect-parentheses                \
  +write mpr:ABC                      \
  -delete 0                           \
    \( mpr:ABC[1x+0+0] \)             \
    \( mpr:ABC \)                     \
    \( mpr:ABC -flop -crop 1x+0+0 \)  \
  +append +repage                     \
  +write mpr:XYZ                      \
  -delete 0                           \
    \( mpr:XYZ[x1+0+0] \)             \
    \( mpr:XYZ \)                     \
    \( mpr:XYZ -flip -crop x1+0+0 \)  \
  -append +repage                     \
  extend.png

Quick-check size of new image

identify rose: extend.png 
 rose:=>ROSE   PPM 70x46 70x46+0+0 8-bit sRGB 9.67KB 0.000u 0:00.000
 extend.png[1] PNG 72x48 72x48+0+0 8-bit sRGB 7.11KB 0.000u 0:00.000

Check, if the top 2 rows of pixels are identical

Top-most row:

convert extend.png[x1+0+0] txt:- | md5sum
 12b639cd1606559633ee23a062ae42a6  -

Second row from top:

convert extend.png[x1+0+1] txt:- | md5sum
 12b639cd1606559633ee23a062ae42a6  -

Third row from top:

convert extend.png[x1+0+2] txt:- | md5sum
 88fc6f9bac3f8ab4cbf2374fbef6fcf0  -

Check, if the two left-most columns are identical

Left-most column:

convert extend.png[1x+0+0] txt:- | md5sum
 4d2223b74901f9ce1fb4456dacb4315c  -

Second column from left:

convert extend.png[1x+1+0] txt:- | md5sum
 4d2223b74901f9ce1fb4456dacb4315c  -

Third column from left:

convert extend.png[1x+2+0] txt:- | md5sum
 07794b4423f5b6515142d15a5c652743  -

I'm not showing how to check if two bottom-most rows and two right-most columns are also identical...


There is also a way to achieve the same effect by using the -virtual-pixel edge operation, but it is more difficult to understand...

Upvotes: 4

Related Questions