Reprinted from alloyteam:http://www.alloyteam.com/2017/06/the-story-of-png-deinterlacing-algorithm/
Objective
The previous article has explained how to parse a PNG image, but the scanning algorithm only illustrates the way of progressive scanning. In fact, PNG also supports an interlaced scanning technique, the ADAM7 interlaced scanning algorithm. Advantages and Disadvantages
What are the benefits of using interlaced scanning? If you have to take a closer look, you will find that there are some PNG images on the network when loading can be done to show a more blurred picture, and then gradually more and more clear, and finally show a complete picture, similar to the following effect:
This is what the interlaced scan can bring. Interlaced scanning will be 1 to 7 scans, each of which is skipped part of the pixel to scan, first scan to the pixel can be rendered first, each more scan, the picture will be clearer, to the last scan will scan all the pixels, and then render the complete picture.
Of course, also because of the jump pixel scan, the whole picture will store more extra data and cause the picture size will be slightly larger, specifically added what additional data will be explained below. Generated
To export a PNG image based on ADAM7 interlaced scanning is very simple, we can use Adobe's artifact--photoshop (PS). We drag a normal picture into PS, then click "File"-"Save for Web format", in the pop-up box select Save As PNG-24
, then tick the staggered , and then click on the storage.
The interleaving here is to only set the scanning algorithm to ADAM7 interlaced, if not tick interleaved, it is normal progressive scan of the PNG image. Principle
The principle of the ADAM7 interlaced scanning algorithm is not difficult, in essence, a PNG image is split into a number of PNG small image, and then a few PNG small images for normal progressive scan parsing, and finally the resolution of the pixel data in accordance with a certain rules of the return to the position.
The image data should be disassembled immediately after extracting it. It's not difficult to split a buffer array that originally stores the image data into multiple buffer arrays. The key question is how to disassemble, when we first sacrifice this picture on the wiki:
The above diagram shows the pixels scanned for each scan, normally a PNG image based on ADAM7 interlaced is going through 7 scans, but some smaller images actually scan less than 7 times, because some scans fail because there are no actual pixels. So the following explanation is still a standard 7 scans to explain, in essence, this algorithm code written out, is compatible with any size of PNG pictures, because the algorithm itself and the size of the picture is irrelevant.
7 scans, in fact, answered the above problem: to split into 7 small pictures. Each small image contains the pixels to be normalized each time it is scanned.
Take the first scan as an example: the rule for the first scan starts at the top left corner (we set this coordinate to (0,0)), then the next point it scans to is the same line at the previous point, which is offset to the right by 8 pixels, i.e. (8,0). And so on, the next point is (16,0), (24,0) and so on. Jumps to the starting point of the next scan line when all the rules-compliant points of the current row are scanned (8,0), that is, the scanned rows for the first scan are also offset by 8 pixels. Until all scan lines have been scanned, we can assume that the scan is over, and consider going to the second scan.
We take an example of a 10*10-sized PNG image, where each number represents a pixel, and the value of the number indicates that the point was scanned at the time of the first scan:
According to the rules, we scan to 4 pixels at the first scan, and we pull the 4 pixels out of the way together, that's the first small picture we're going to take apart:
In other words, our first small image is a PNG image of 2*2 size. The size of the next small figure and so on, so that we can know the basis for the demolition.
As mentioned above, the disassembly is essentially a buffer array to store the image data to be sliced, the buffer object in the Nodejs has a very useful method--slice, its usage and the same method as the same as the group.
Directly with the above example, our first small image is the 2*2 point PNG image, assuming that we have a pixel of 3 bytes, then we have to cut out the length of the first buffer subarray is 2*(2*3+1)
. Perhaps someone is curious, why is multiplied 2*3+1
instead of directly multiplied? 2*3
As we mentioned before, the small figure after the small map to perform a normal progressive scan parsing, so that the first byte of each row is not actually stored in the image data, but the filter type, so that each row of the bytes required to be 2*3
added 1 on the basis.
Other small graph splitting method is the same, after the last scan is finished, we will get 7 small pictures. Then we follow the above rules to the pixels of the small map to the position, that is, the meaning of filling back. Below is a brief demonstration of the process of homing:
Once the pixels of the 7 thumbnails are all in place, we can finally get a full PNG image.
The code for the entire process is as follows:
let width; Full image width, parse IHDR data block can be
let height; Full image height, parse IHDR data block can be
let colors; Number of channels, parsing the IHDR data block can be
Let BitDepth; Image depth, parsing IHDR data blocks can be
let data; Full image data
Let Bytesperpixel = Math.max (1, colors * BITDEPTH/8); Number of bytes per pixel
Let Pixelsbuffer = Buffer.alloc (bytesperpixel * Width * height, 0xFF); Used to store the last parsed image data.
Rules for 7 scans
Let StartX = [0, 0, 4, 0, 2, 0, 1];
Let IncX = [8, 8, 8, 4, 4, 2, 2];
Let Starty = [0, 4, 0, 2, 0, 1, 0];
Let incy = [8, 8, 4, 4, 2, 2, 1];
Let offset = 0; Record the start position of the small graph
7 Scans
for (let i=0; i<7; i++) {
Sub-image information
Let Subwidth = Math.ceil ((width-starty[i])/incy[i], 10); Small chart width
Let Subheight = Math.ceil ((height-startx[i])/incx[i], 10); Small figure height
Let Subbytesperrow = Bytesperpixel * subwidth; Small figures per line of bytes
Let Offsetend = offset + (Subbytesperrow + 1) * subheight; Small plot End position
Let Subdata = Data.slice (offset, offsetend); Small Image voxel Data
Normal progressive scan of small graphs
Let Subpixelsbuffer = This.interlacenone (Subdata, Subwidth, Subheight, Bytesperpixel, Subbytesperrow);
Let Suboffset = 0;
Pixel-homing
For (let X=startx[i]; x
For (let Y=starty[i]; y<width; Y+=incy[i]) {
Copy back to the original location by pixel
for (let z=0; z<bytesperpixel; z++) {
pixelsbuffer[(x * width + y) * Bytesperpixel + z] = subpixelsbuffer[suboffset++] & 0xFF;
}
}
}
offset = offsetend; To the beginning of the next small chart
}
return pixelsbuffer; the end
The process for the entire ADAM7 interlaced scan is probably this:
The earlier mention of PNG images based on this type of scan tends to be larger, because the picture stores some extra data. The additional data here refers to the type of filtering . The original PNG large map into a small map, the number of scan lines will rub against the rise, the first byte of each scan line is used to store the filter type, so the more rows, the more the additional data will be more. As for the use of PNG images and other time to choose which kind of scanning methods and other pictures, it will depend on the specific scene. If you are interested in the full code, you can poke here.
Resources:
- https://www.w3.org/TR/PNG/
- http://www.libpng.org/pub/png/
- Https://en.wikipedia.org/wiki/Portable_Network_Graphics
?
The story of PNG: interlaced scanning algorithm