Source code address: github.com/weiruifeng/… Digital Image Processing refers to Processing by computer. Speaking of digital image processing, we all think that C++ has many libraries and algorithms, and MATLAB is convenient. However, since the advent of canvas, JavaScript can carry out pixel-level operations on images, and even directly process the original binary data of images.
Get data and save pictures
To get the data
Use fileReader and Canvas to get the image
<canvas id="myCanvas">Sorry, your browser does not yet support Canvas.</canvas>
<input type="file" id="myFile" />
Copy the code
When the user selects the image
file.onchange = function(event) {
const selectedFile = event.target.files[0];
const reader = new FileReader();
reader.onload = putImage2Canvas;
reader.readAsDataURL(selectedFile);
}
function putImage2Canvas(event) {
const img = new Image();
img.src = event.target.result;
img.onload = function(){
myCanvas.width = img.width;
myCanvas.height = img.height;
var context = myCanvas.getContext('2d');
context.drawImage(img, 0.0);
const imgdata = context.getImageData(0.0, img.width, img.height);
/ / imgdata processing}}Copy the code
Where, the ImageData object stores the real pixel data of the Canvas object, which contains three read-only attributes: **width: ** image width, in pixels **height: ** image height, in pixels **data: **Uint8ClampedArray one-dimensional array containing RGBA integer data ranging from 0 to 255 inclusive **Uint8ClampedArray digital image processing application data is imagedata.data data
Save the picture
HTMLCanvasElement provides a toDataURL method that is very useful when saving images. It returns a data link containing the image presentation format specified by the type parameter. The format of the data link is
data:[<mediatype>][;base64],<data>
Copy the code
Mediatype is a string of MIME type, such as “image/ JPEG “for A JPEG image file. If omitted, the default is text/plain; charset=US-ASCII
You can download it by using the DOWNLOAD attribute of the A tag in HTML
downloadFile(fileName, url) {
const aLink = document.createElement('a');
aLink.download = fileName;
aLink.href = url;
aLink.click();
}
// Download the image
downloadFile(fileName, myCanvas.toDataURL());
Copy the code
Point of operation
Point Operation enables users to change the gray scale range occupied by image data, which can be regarded asFrom pixel to pixelThe copy operation of. If the input image is, the output image is), point operation can be expressed as:
The putIt is called gray transformation function, which describes the conversion relationship between input gray value and output gray value. Once the gray transformation function is determined, the point operation is completely determined.
The general operations of point operation include gray equalization, linear transformation, threshold transformation, window transformation, gray stretching, etc.
Gray histogram
An overview of the
The gray histogram is used to count the number or proportion of pixels (0 ~ 255) in a gray image. Graphically, the gray histogram is a two-dimensional graph. The abscissa represents the gray value (gray level), and the ordinate represents the number or probability of pixels with each gray value or gray level appearing in the image.
code
/** ** data (grayscale image) * @param data raw data * @param strength split * @returns {Array} */
function statistics(data, strength = 1) {
const statistArr = [];
for (let i = 0, len = data.length; i < len; i += 4) {
const key = Math.round(data[i] / strength);
statistArr[key] = statistArr[key] || 0;
statistArr[key]++;
}
return statistArr;
}
Copy the code
The histogram shows the distribution of pixels in an image.
Histogram equalization
An overview of the
We all know that the higher the contrast, the sharper the image will be, and the lower the contrast, the more gray the image will be. Contrast, the ratio of black to white in a gray image, is the gradation from black to white. The greater the ratio, the more gradations from black to white, and thus the richer the colors.
Histogram equalization is a method to adjust contrast by using image histogram in the field of image processing. The goal is to have the same number of pixels on the image, which will make the image clearer if the background and foreground are too bright or too dark.
Theoretical basis
Consider a discrete grayscale image {x}, letGray saidThe number of occurrences, so that the grayscale in the image isThe occurrence probability of pixels of is:
Is all grayscale numbers in the image (usually 256),Is the number of pixels in the image,Actually, the pixel value is zeroHistogram of the image, normalized to.
The corresponding to theThe cumulative distribution function of, is defined as:
Is a cumulative normalized histogram of the image.
Let’s create a form of thetaFor each value in the original imageSo that theThe cumulative probability function of can be linearized in all value ranges, and the transformation formula is defined as:
For constantIn image processing, 256.
willPut in (3) to get:
Calculate (3) and (4) as follows:
Formula 5 is the relationship between the original pixel and the transformed pixel.
code
/** * this function is used to equalize the image histogram * @param data */
function inteEqualize(data) {
// Grayscale mapping table
const bMap = new Array(256);
// Grayscale mapping table
const lCount = new Array(256);
for (let i = 0; i < 256; i++) {
/ / reset
lCount[i] = 0;
}
// Calculate the count of each grayscale value (grayscale image only)
for (let i = 0, len = data.length; i < len; i += 4) {
lCount[data[i]]++;
}
// Calculate the grayscale mapping table
for (let i = 0; i < 256; i++) {
let lTemp = 0;
for (let j = 0; j < i; j++) {
lTemp += lCount[j];
}
// Calculate the corresponding new gray value
bMap[i] = Math.round(lTemp * 255 / (data.length / 4));
}
/ / assignment
for (let i = 0, len = data.length; i < len; i += 4) {
data[i] = bMap[data[i]];
data[i + 1] = bMap[data[i + 1]];
data[i + 2] = bMap[data[i + 2]]. }}Copy the code
Gray linear transformation
Theoretical basis
The linear transformation of grayscale is to transform the grayscale of all points in the image in accordance with the linear grayscale transformation function, the linear grayscale transformation functionIs a one-dimensional linear function:
Gray scale transformation equation is:
In the type parameterIs the slope of a linear function,Is a linear function atThe intercept of the axis,Denotes the gray level of the input image,Represents the grayscale of the output image.
Grayscale images have the following rules:
- when, the output image when the contrast will increase; When fWhen, the contrast of the output image will decrease;
- whenandWhen not equal to 0, the operation only makes all pixels move up or down when the gray value, the effect is to make the whole image darker or brighter;
- if, dark area will become brighter, bright area will become darker, dot operation completes the image complement operation;
- if, the output image is the same as the input image;
- if, the grayscale of the output image is just reversed;
code
/** * this function is used for image grayscale * @param data * @param fA linear transformation slope * @param fB linear transformation intercept */
function linerTrans(data, fA, fB) {
for (let i = 0, len = data.length; i < len; i += 4) {
// Convert to RGB three
for (let j = 0; j < 3; j++) {
let fTemp = fA * data[i + j] + fB;
if (fTemp > 255) {
fTemp = 255;
} else if (fTemp < 0) {
fTemp = 0;
} else {
fTemp = Math.round(fTemp); } data[i + j] = fTemp; }}}Copy the code
Gray threshold transformation
Theoretical basis
Gray threshold transformation can transform a gray image into black and white binary image. A threshold is set by the user in advance. If the gray value of a pixel in the image is less than the threshold, the gray value of the pixel is set to 0, otherwise 255.
code
* @param data * @param bthre threshold */
function thresholdTrans(data, bthre) {
for (let i = 0, len = data.length; i < len; i += 4) {
// Convert to RGB three
for (let j = 0; j < 3; j++) {
if (data[i + j] < bthre) {
data[i + j] = 0;
} else {
data[i + j] = 255; }}}}Copy the code
Gray window transformation
Theoretical basis
Gray window transformation limits a window range, the gray value in the window remains unchanged; The gray value less than the lower limit of the window is directly set to 0; The gray value greater than the upper limit of the window is directly set to 255.
The transformation function expression of gray window transformation is as follows:
Type,Represents the lower limit of the window,Represents the upper limit of a window.
Grayscale window transform can be used to remove the background that is light and the object that is dark image background.
code
/** * this function is used to transform the image window. Only within the window range the grayscale remains constant * @param data * @param bLow lower limit * @param bUp upper limit */
function windowTrans(data, bLow, bUp) {
for (let i = 0, len = data.length; i < len; i += 4) {
// Convert to RGB three
for (let j = 0; j < 3; j++) {
if (data[i + j] < bLow) {
data[i + j] = 0;
} else if (data[i + j] > bUp) {
data[i + j] = 255; }}}}Copy the code
Grayscale stretching transformation function
Grayscale stretching is similar to grayscale linear transformation, but the difference is that grayscale stretching is not a complete linear transformation, but a piecewise linear transformation.
The function expression is as follows:
Gray scale transformation function is shown as follows:
code
/** * this function is used to grayscale stretch the image ** this function results in the original image between x1 and x2 between y1 and y2 * @param data * @param bx1 grayscale stretch the first point X coordinates * @param by1 The Y coordinate of the first point of gray stretching * @param BX2 gray stretching the X coordinate of the second point * @param BY2 gray stretching the Y coordinate of the second point */
function grayStretch(data, bx1, by1, bx2, by2) {
// Grayscale mapping table
const bMap = new Array(256);
for (let i = 0; i < bx1; i++) {
// prevent the denominator from being 0
if (bx1 > 0) {
// Linear transformation
bMap[i] = Math.round(by1 * i / bx1);
} else {
bMap[i] = 0; }}for (let i = bx1; i < bx2; i++) {
// check whether bx1 is equal to bx2.
if(bx2 ! == bx1) { bMap[i] =Math.round((by2 - by1) * (i - bx1) / (bx2 - bx1));
} else {
// Assign by1 directlybMap[i] = by1; }}for (let i = bx2; i < 256; i++) {
// check whether bx2 equals 256(to prevent 0 in the denominator)
if(bx2 ! = =255) {
// Linear transformation
bMap[i] = by2 + Math.round((255 - by2) * (i - bx2) / (255 - bx2));
} else {
// Assign 255 directly
bMap[i] = 255; }}for (let i = 0, len = data.length; i < len; i += 4) {
data[i] = bMap[data[i]];
data[i + 1] = bMap[data[i + 1]];
data[i + 2] = bMap[data[i + 2]]. }}Copy the code
Geometric transformation of the image
Canvas in HTML5 has a perfect image processing interface. We can directly use canvas interface for geometric transformation of images. Here are a few simple examples of geometric transformation interfaces:
-
Image translation
context.translate(x, y); Copy the code
-
Image zooming
context.scale(scalewidth, scaleheight); Copy the code
-
Image transform
There are no methods for image transformations in canvas, but don’t worry, we haven’t touched on pixel-level operations yet. In the previous section, related content of image scaling was introduced. It was mentioned that when the absolute value of scaleWidth and scaleheight is greater than 1, it is enlarged, and when the absolute value of scaleheight is less than 1, it is reduced, but the positive and negative values were not mentioned.
content.translate(myCanvas.width/2, myCanvas.height/2); content.scale(- 1.1); content.translate(myCanvas.width/2, myCanvas.height/2); content.drawImage(img, 10.10); Copy the code
-
The image rotation
context.rotate(angle); Copy the code
-
Image transpose
Canvas does not provide special methods for image transpose, but we can use the combination of rotation and mirror to achieve the purpose of image transpose. The transpose of an image can be decomposed into a horizontal rotation followed by 90° clockwise, or a vertical rotation followed by 90° counterclockwise. The following image transpose operation is realized by rotating 90° clockwise and then flipping horizontally
context.translate(myCanvas.width/2, myCanvas.height/2); context.scale(- 1.1); context.rotate(90*Math.PI/180); context.translate(-myCanvas.width/2, -myCanvas.height/2); context.drawImage(img, 10.10); Copy the code
Image enhancement
Image enhancement is to selectively highlight the interesting parts of the image, while attenuation is secondary to information, so as to improve the readability of the image. The common purpose is to highlight the contour of the target, attenuate various noises and so on.
There are usually two kinds of image enhancement methods: spatial domain method and frequency domain method. The spatial domain method mainly deals with the gray value of image pixel directly in the spatial domain. This chapter covers only the spatial domain method.
Image enhancement techniques such as spatial domain method can be described by the following formula:
Among themIs the image before processing,Represents the processed image,Is a spatial operation function.
Image gray correction
The gray correction of images is based on the different degradation of images and different correction methods are adopted. Common methods refer to the method inside the point operation.
Template operation
Template is a matrix square, and template operation can be regarded as a process of weighted summation. Each pixel in the image area used is multiplied by each element in the matrix square. The sum of all products is the new value of the center pixel of the region, which is often used in digital image processing. Image smoothing, sharpening, refining and edge detection all use template operations.
For example, a common smoothing algorithm is to add the gray value of a pixel in the original image to the gray value of its eight neighboring pixels, and then take the average value (divided by 9) as the gray value of the pixel in the new image. It is expressed as follows:
When using a template to process an image, pay attention to the boundary problem, because the template will generate an error when processing the boundary. The common handling methods are as follows:
- Ignore boundary pixels, that is, the processed pixels will lose those pixels.
- Retain the original boundary pixels, that is, copy the boundary pixels into the processed image.
Commonly used template
-
Low pass filter
-
High pass filter
-
Translation and differential edge detection
-
Matched filtering edge detection
-
Edge detection
-
Gradient direction detection
code
@param data @param lWidth image width @param lHeight Image height @param tempObj template data @param tempobj.itempw Template width * @param Tempobj.itemph Template height * @param Tempobj.itempmx Template center element X * @param Tempobj.itempmy template center element Y * @param @param Tempobj.fcoef Template coefficient */
function template(data, lWidth, lHeight, tempObj) {
const { iTempW, iTempH, iTempMX, iTempMY, fpArray, fCoef } = tempObj;
// Save the raw data
const dataInit = [];
for (let i = 0, len = data.length; i < len; i++) {
dataInit[i] = data[i];
}
// line (remove edge lines)
for (let i = iTempMY; i < lHeight - iTempMY - 1; i++) {
// select * from the column where the edge is
for (let j = iTempMX; j < lWidth - iTempMX - 1; j++) {
const count = (i * lWidth + j) * 4;
const fResult = [0.0.0];
for (let k = 0; k < iTempH; k++) {
for (let l = 0; l < iTempW; l++) {
const weight = fpArray[k * iTempW + l];
const y = i - iTempMY + k;
const x = j - iTempMX + l;
const key = (y * lWidth + x) * 4;
// Save the pixel value
for (let i = 0; i < 3; i++) { fResult[i] += dataInit[key + i] * weight; }}}for (let i = 0; i < 3; i++) {
// Multiply the coefficient
fResult[i] *= fCoef;
// take the absolute value
fResult[i] = Math.abs(fResult[i]);
fResult[i] = fResult[i] > 255 ? 255 : Math.ceil(fResult[i]);
// Put the modified value backdata[count + i] = fResult[i]; }}}}Copy the code
The boundary is processed in the code using the original boundary pixel.
Smooth and sharpen
The idea of smoothing is to remove the sudden change of points through the operation of one point and several points around, so as to filter out a certain degree of noise, but the image has a certain degree of blur, the commonly used template is the template of low-pass filter.
The purpose of sharpening is to make a blurred image clearer. The essence of the fuzzy image is the image by the average or integral operation, so the image can be reversed such as differential operation to make the image clear. From the perspective of spectrum, the essence of image blur is that its high-frequency components are attenuated, so the image can be clear by high-pass filtering. Sharpening process will also amplify the noise of the image, so it is generally to remove or reduce the noise before sharpening.
There are generally two methods of image sharpening: calculus and high-pass filtering. For high-pass filtering, refer to the high-pass filtering template. Differential sharpening introduces Laplacian sharpening.
Gradient sharpening
Set the image asTo defineIn the pointThe gradient vector of phiTo:
Gradients have two important properties:
The direction of the gradient is in the delta functionIn the direction of maximum rate of change
The magnitude of the gradient is usedIs, and its value is:
This equation leads to the conclusion that the value of the gradient isThe increase in distance per unit in the direction of its maximum rate of change.
For discrete digital images, the above equation can be rewritten as:
For the convenience of calculation, the following approximate calculation formula can also be used:
This gradient method is also known as horizontal and vertical difference method, and there is another way to calculate the difference alternately, called Robert gradient method:
The absolute difference algorithm is used to approximate:
Because the gradient is small where the image changes slowly, the image will appear dark, and the usual practice is to give a thresholdIf theThe value is smaller than the threshold, keep the original gray value unchanged; If the value is greater than or equal to the threshold, the value is assigned to:
The algorithm code based on horizontal and vertical difference method is as follows:
@param data * @param lWidth * @param lHeight height * @param bThre threshold */
function gradSharp(data, lWidth, lHeight, bThre) {
// Save the raw data
const dataInit = [];
for (let i = 0, len = data.length; i < len; i++) {
dataInit[i] = data[i];
}
for (let i = 0; i < lHeight - 1; i++) {
for (let j = 0; j < lWidth - 1; j++) {
const lpSrc = (i * lWidth + j) * 4;
const lpSrc1 = ((i + 1) * lWidth + j) * 4;
const lpSrc2 = (i * lWidth + j + 1) * 4;
for (let i = 0; i < 3; i++) {
const bTemp = Math.abs(dataInit[lpSrc + i] - dataInit[lpSrc1 + i]) +
Math.abs(dataInit[lpSrc + i] - dataInit[lpSrc2 + i]);
if (bTemp >= 255) {
data[lpSrc + i] = 255;
// Check whether the value is greater than the threshold. If the value is smaller than the threshold, the gray value remains unchanged
} else if (bTemp >= bThre) {
data[lpSrc + i] = bTemp;
}
}
}
}
}
Copy the code
Laplace sharpening
As we know, the first derivative of a function describes the growth or decrease of the graph of the function, while the second derivative describes the speed of the change of the graph, such as sharp growth or decrease or gentle growth or decrease. The Laplace operation is also a linear combination of partial derivatives, and an isotropic linear operation.
setIs the Laplace operator, then:
For discrete digital images, the first partial derivative is:
Then its second partial derivative is:
So, the Laplace operatorTo:
For image blur caused by diffusion phenomenon, the following formula can be used to sharpen:
hereIs the coefficient related to the diffusion effect. The coefficient has to be reasonable ifIf it is too large, the image contour edge will overshoot. Conversely, ifToo small, the sharpening effect is not obvious.
If the, then the transformation formula is:
This variation produces a template matrix:
In fact, there is another form of Laplacian sharpening that we often use:
Code reference code in the template.
Median filtering
The principle of
Median filtering is a kind of nonlinear digital filter technology. It generally adopts a sliding window with odd points, and replaces the gray value of a fixed point (usually the center point of the window) with the median gray value of each point in the window. For an odd number of elements, the median value refers to the middle value after sorting by size; for an even number of elements, the median value refers to the average value of the middle two gray values after sorting.
Median filtering is a common step in image processing. It is especially useful for speckle noise and salt and pepper noise.
code
/** * median filter * @param data * @param lWidth image width * @param lHeight image height * @param filterObj template data * @param Filterobj. iFilterW Template width * @param Filterobj. iFilterH Template height * @param Filterobj. iFilterMX Template center X coordinate * @param Filterobj. iFilterMY template center element Y */
function medianFilter(data, lWidth, lHeight, filterObj) {
const { iFilterW, iFilterH, iFilterMX, iFilterMY } = filterObj;
// Save the raw data
const dataInit = [];
for (let i = 0, len = data.length; i < len; i++) {
dataInit[i] = data[i];
}
// line (remove edge lines)
for (let i = iFilterMY; i < lHeight - iFilterH - iFilterMY - 1; i++) {
for (let j = iFilterMX; j < lWidth - iFilterW - iFilterMX - 1; j++) {
const count = (i * lWidth + j) * 4;
const fResult = [[], [], []];
for (let k = 0; k < iFilterH; k++) {
for (let l = 0; l < iFilterW; l++) {
const y = i - iFilterMY + k;
const x = j - iFilterMX + l;
const key = (y * lWidth + x) * 4;
// Save the pixel value
for (let i = 0; i < 3; i++) { fResult[i].push(dataInit[key + i]); }}}// Put the median back
for (let w = 0; w < 3; w++) { data[count + w] = getMedianNum(fResult[w]); }}}}/ * * * array sorted for intermediate values * @ param bArray * @ returns {* | number} * /
function getMedianNum(bArray) {
const len = bArray.length;
bArray.sort();
let bTemp = 0;
// Calculate the median value
if ((len % 2) > 0) {
bTemp = bArray[(len - 1) / 2];
} else {
bTemp = (bArray[len / 2] + bArray[len / 2 - 1) /2;
}
return bTemp;
}
export { medianFilter };
Copy the code
Image morphology
The theoretical basis of morphology is set theory. Mathematical morphology presents a set of unique transformation and operation methods. Let’s look at some of the most basic mathematical morphological operations.
For a given target imageAnd a structural elementImagine thatMove on the image. At each of the current positions.There are only three possible states:
- 与Is not empty
As shown in the figure:
The first case is explainedwithMaximum correlation; The second case is explainedwithNot relevant; And the third case sayswithOnly partially.
Corrosion and expansion
The principle of
When condition one is satisfiedIs the maximum correlation point set between all the structural elements and the image. We call this point setrightWhen all the constituent elements of point x satisfying conditions 1 and 2 meet the maximum correlation point set of the image, we call this point set asrightThe inflation. Simply put, corrosion can be seen as putting imagesEach with a structural elementAn congruent subsetContract for the pointAnd the expansion will beEach of these pointsExpand to.
The operation of corrosion and expansion is to perform set operation on image X with a given template, as shown in the figure:
code
Code for binary image corrosion and expansion algorithm.
/** * This function is used to corrode the image. * Structural elements are three points in horizontal or vertical directions, with the middle point at the origin; * Or a user-defined 3*3 structure element. * @param data image data * @param lWidth Original image width (pixel number) * @param lHeight Original image height (pixel number) * @param nMode Corrosion mode, 0 represents horizontal direction, 1 represents vertical direction, 2 represents custom structural elements * @param structure custom 3*3 structural elements */
function erosionDIB(data, lWidth, lHeight, nMode, structure) {
// Save the raw data
const dataInit = [];
for (let i = 0, len = data.length; i < len; i++) {
dataInit[i] = data[i];
}
if (nMode === 0) {
// Use horizontal structural elements for corrosion
for (let j = 0; j < lHeight; j++) {
// The leftmost and rightmost columns of pixels are not processed to prevent transgression due to the use of 1*3 structure elements
for (let i = 1; i < lWidth - 1; i++) {
const lpSrc = j * lWidth + i;
for (let k = 0; k < 3; k++) {
// If the current point in the original image is not black, then the current point in the target image is white
for (let n = 0; n < 3; n++) {
const pixel = lpSrc + n - 1;
data[lpSrc * 4 + k] = 0;
if (dataInit[pixel * 4 + k] === 255) {
data[lpSrc * 4 + k] = 255;
break;
}
}
}
}
}
} else if (nMode === 1) {
// Use vertical structural elements for corrosion
// The top and bottom two columns of pixels are not processed to prevent overstepping due to the use of 1*3 structure elements
for (let j = 1; j < lHeight - 1; j++) {
for (let i = 0; i < lWidth; i++) {
const lpSrc = j * lWidth + i;
for (let k = 0; k < 3; k++) {
// If the current point in the original image is not black, then the current point in the target image is white
for (let n = 0; n < 3; n++) {
const pixel = (j + n - 1) * lWidth + i;
data[lpSrc * 4 + k] = 0;
if (dataInit[pixel * 4= = =255) {
data[lpSrc * 4 + k] = 255;
break;
}
}
}
}
}
} else {
// Due to the use of a 3*3 structure element, the leftmost and rightmost columns of pixels and the top and bottom columns of elements are not processed to prevent overreaching
for (let j = 1; j < lHeight - 1; j++) {
for (let i = 1; i < lWidth - 1; i++) {
const lpSrc = j * lWidth + i;
for (let k = 0; k < 3; k++) {
data[lpSrc * 4 + k] = 0;
// If one of the black points in the corresponding structure element in the original image is not black, then the current point in the target image is assigned white
for (let m = 0; m < 3; m++) {
for (let n = 0; n < 3; n++) {
if (structure[m][n] === - 1) {
continue;
}
const pixel = lpSrc + ((2 - m) - 1) * lWidth + (n - 1);
if (dataInit[pixel * 4= = =255) {
data[lpSrc * 4 + k] = 255;
break;
}
}
}
}
}
}
}
}
/** * This function is used to expand the image. * Structural elements are three points in horizontal or vertical directions, with the middle point at the origin; * Or a user-defined 3*3 structure element. * @param data image data * @param lWidth Original image width (pixel number) * @param lHeight Original image height (pixel number) * @param nMode Corrosion mode, 0 represents horizontal direction, 1 represents vertical direction, 2 represents custom structural elements * @param structure custom 3*3 structural elements */
function dilationDIB(data, lWidth, lHeight, nMode, structure) {
// Save the raw data
const dataInit = [];
for (let i = 0, len = data.length; i < len; i++) {
dataInit[i] = data[i];
}
if (nMode === 0) {
// Use horizontal structural elements for corrosion
for (let j = 0; j < lHeight; j++) {
// The leftmost and rightmost columns of pixels are not processed to prevent transgression due to the use of 1*3 structure elements
for (let i = 1; i < lWidth - 1; i++) {
const lpSrc = j * lWidth + i;
for (let k = 0; k < 3; k++) {
// If the current point in the original image is not black, then the current point in the target image is white
for (let n = 0; n < 3; n++) {
const pixel = lpSrc + n - 1;
data[lpSrc * 4 + k] = 255;
if (dataInit[pixel * 4 + k] === 0) {
data[lpSrc * 4 + k] = 0;
break;
}
}
}
}
}
} else if (nMode === 1) {
// Use vertical structural elements for corrosion
// The top and bottom two columns of pixels are not processed to prevent overstepping due to the use of 1*3 structure elements
for (let j = 1; j < lHeight - 1; j++) {
for (let i = 0; i < lWidth; i++) {
const lpSrc = j * lWidth + i;
for (let k = 0; k < 3; k++) {
// If the current point in the original image is not black, then the current point in the target image is white
for (let n = 0; n < 3; n++) {
const pixel = (j + n - 1) * lWidth + i;
data[lpSrc * 4 + k] = 255;
if (dataInit[pixel * 4= = =0) {
data[lpSrc * 4 + k] = 0;
break;
}
}
}
}
}
} else {
// Due to the use of a 3*3 structure element, the leftmost and rightmost columns of pixels and the top and bottom columns of elements are not processed to prevent overreaching
for (let j = 1; j < lHeight - 1; j++) {
for (let i = 1; i < lWidth - 1; i++) {
const lpSrc = j * lWidth + i;
for (let k = 0; k < 3; k++) {
data[lpSrc * 4 + k] = 255;
// If one of the black points in the corresponding structure element in the original image is not black, then the current point in the target image is assigned white
for (let m = 0; m < 3; m++) {
for (let n = 0; n < 3; n++) {
if (structure[m][n] === - 1) {
continue;
}
const pixel = lpSrc + ((2 - m) - 1) * lWidth + (n - 1);
if (dataInit[pixel * 4= = =0) {
data[lpSrc * 4 + k] = 0;
break;
}
}
}
}
}
}
}
}
Copy the code
Open and closed operations
Corrosion, as we know, is a process of eliminating boundary points and making boundaries shrink inward, and can be used to eliminate small and meaningless objects. Expansion is the process of merging all the background points in contact with the object into the object so that the boundary expands outwards, which can be used to fill the void in the object.
The process of corrosion followed by expansion is called open operation. It is used to eliminate small objects, separate objects at slender points, and smooth the boundaries of larger objects without significantly changing their area; The process of expansion followed by corrosion is called closed operation. It is used to fill small cavities in the body, connect adjacent objects, and smooth their boundaries without significantly changing their area.
Open and closed operations are a combination of corrode and bloat, so the code can reference corrode and bloat code.
elaboration
Refinement is to find the central axis or skeleton of a figure or stroke and replace the figure or stroke with its skeleton. In word recognition or image understanding, thinning the processed image first helps to highlight and reduce redundant information.
The following is a specific thinning algorithm (Zhang fast parallel thinning algorithm) :
One of the imagesRegion, marking the name of each point, where P1 is located in the center. As shown in the figure:
if(black dot), if the following four conditions are met at the same time, then delete.
Among themisIs the number of non-zero adjacent points,Based on.When p9 is in order, the values of these points change fromtoThe number of changes.
Repeat this step for each point in the image until all points are unerasable.
code
/** * * @param data * @param lWidth original image width (pixels) * @param lHeight Original image height (pixels) * /
function thinDIB(data, lWidth, lHeight) {
// Save the raw data
const dataInit = [];
for (let i = 0, len = data.length; i < len; i++) {
dataInit[i] = data[i];
}
let bModified = true;
const neighBour = [
[0.0.0],
[0.0.0],
[0.0.0]].while (bModified) {
bModified = false;
for (let j = 1; j < lHeight - 1; j++) {
for (let i = 1; i < lWidth - 1; i++) {
let bCondition1 = false;
let bCondition2 = false;
let bCondition3 = false;
let bCondition4 = false;
const lpSrc = j * lWidth + i;
// If the current point in the original image is white, skip it
if (dataInit[lpSrc * 4]) {
continue;
}
// Get the pixel value in the 3*3 area adjacent to the current point, 0 represents white, 1 represents black
const bourLength = 3;
for (let m = 0; m < bourLength; m++) {
for (let n = 0; n < bourLength; n++) {
const pixel = lpSrc + ((2 - m) - 1) * lWidth + (n - 1);
neighBour[m][n] = (255 - dataInit[pixel * 4])?1 : 0; }}const borderArr = [neighBour[0] [1], neighBour[0] [0], neighBour[1] [0], neighBour[2] [0],
neighBour[2] [1], neighBour[2] [2], neighBour[1] [2], neighBour[0] [2]].let nCount1 = 0;
let nCount2 = 0;
for (let i = 0, len = borderArr.length; i < len; i++) {
nCount1 += borderArr[i];
if (borderArr[i] === 0 && borderArr[(i + 1) % len] === 1) { nCount2++; }}NZ(P1)<=6
if (nCount1 >= 2 && nCount1 <= 6) {
bCondition1 = true;
}
Z0(P1) = 1
if (nCount2 === 1) {
bCondition2 = true;
}
/ / P2 * P4 * P8 = 0
if (borderArr[0] * borderArr[2] * borderArr[6= = =0) {
bCondition3 = true;
}
/ / P2 * P4 * P6 = 0
if (borderArr[0] * borderArr[2] * borderArr[4= = =0) {
bCondition4 = true;
}
for (let k = 0; k < 3; k++) {
if (bCondition1 && bCondition2 && bCondition3 && bCondition4) {
data[lpSrc * 4 + k] = 255;
bModified = true;
} else {
data[lpSrc * 4 + k] = 0; }}}}if (bModified) {
for (let i = 0, len = data.length; i < len; i++) { dataInit[i] = data[i]; }}}}Copy the code
Edges, contours and fillings
Edge detection
The edge of an image is the most basic feature of the image. The so-called edge refers to the set of pixels around which the gray level of the pixels has a step change or the roof change. The types of edges can be divided into two types: one is called step edge, and the gray values of pixels on both sides are significantly different; The other is called the roof edge, which is located at the turning point of gray value from increase to decrease to change.
Edge detection operator detects each pixel to the neighborhood and quantifies the change rate of gray level, including direction determination. Most convolution methods based on directional derivative masks are used. Here are several commonly used edge detection operators:
-
Roberts edge detection operator:
Roberts edge detection operator is a kind of edge detection operator using local difference operator. It is given by the following formula:
Where F (x, y) is the input image with integer pixel coordinates, the square root operation makes this processing similar to the process that occurs in the human visual system.
-
Sobel edge operator
The above two convolution kernels form the Sobel edge operator. Each point in the image is convolved with these two kernels. One has the greatest influence on the normal vertical edge, while the other has the greatest influence on the horizontal edge. The maximum value of the two winders is the output bit of this point.
-
Prewitt edge operator
The above two convolution kernels form the Prewitt edge operator. Just like using the Sobel operator, each point in the image is convolved with these two kernels, and the maximum value is taken as the output. The Prewitt operator also produces an edge amplitude image.
-
Krisch edge operator
The eight convolution kernels above form the Kirsch edge operator. Each point in the image is convolved with eight masks, each with a maximum response to a particular edge direction. The maximum in all eight directions is used as the output of the edge amplitude image. The serial number of the maximum response mask constitutes the serial number of the edge direction.
-
Gauss-laplacian operator
A Laplace operator is a second derivative operator that operates on a two-dimensional function. The usual Laplace operator used is as follows:
Comparison of edge detection operators
operator | Advantages and disadvantages comparison |
---|---|
Roberts | It is better to process the image with steep low noise, but the edge extracted by Roberts operator is thicker, so the edge location is very accurate. |
Sobel | For gray gradient and more noise image processing effect is better, Sobel operator is more accurate edge positioning. |
Prewit | For gray gradient and more noise image processing effect is better |
Kirsch | For gray gradient and more noise image processing effect is better |
Gauss-Laplace | Accurate positioning of stage edge points in the image, very sensitive to noise, loss of part of the direction of edge information, resulting in some discontinuous edge detection. |
Contour extraction and contour tracking
The purpose of contour extraction and contour tracking is to obtain the external contour features of the image. The algorithm of binary image contour extraction is very simple, that is, hollowing out the internal point: if a point in the original image is black and all its 8 adjacent points are black (this point is the internal point), the point will be deleted. The content of morphology is to use a nine-point structural element to corrode the original image, and then subtract the corroded image from the original image.
Image contour extraction image comparison:
Contour tracking is to track the boundary by finding the edge points in sequence. First, search from left to right and bottom to top, and the first black dot found must be the bottom-left boundary point, denoted as A. At least one of its four neighboring points on the right, upper right, upper left is the boundary point, denoted as B. Starting from B, search for the boundary point C in the adjacent points in the order of right, upper right, upper left, upper left, lower left, lower right. If C is point A, it means that it has gone round and the program is finished; Otherwise, keep looking from C until you find A. It is easy to determine whether it is a boundary point: it is a boundary point if none of its four neighbors are black.
This method requires judgment of eight points around each boundary pixel, which requires a large amount of calculation. There is another tracking guideline:
First, find the bottom-left boundary point according to the above method. Starting with this boundary point, assume that all boundary points have been found in a clockwise circle around the entire image. Since the boundary is continuous, each boundary point can be represented by the Angle that this boundary point extends with respect to the previous one. Therefore, the following tracing criteria can be used: starting from the first boundary point, define the initial search direction as along the upper left; If the point on the upper left is a black point, it is the boundary point. Otherwise, rotate 90 degrees counterclockwise based on the search direction, and continue to search the next black point in the same way until the original multi-boundary point is returned.
The contour tracking algorithm is shown as follows:
Seed filling
Seed filling algorithm is an algorithm in graphics and the reverse operation of contour extraction algorithm.
The seed filling algorithm assumes that a certain point in the closed contour is known, and then searches for points adjacent to the seed point and located in the contour. If the adjacent points are not in the contour, the boundary of the contour is reached. If the adjacent point is within the contour, this point becomes a new seed point and the search continues.
The algorithm flow is as follows:
- Seed pixels are pushed onto the stack;
- When the stack is not empty, push a pixel out of the stack and set the pixel to the desired value;
- For each four-connected or eight-connected pixel adjacent to the current pixel, the above two parts are tested;
- If the pixel under test has not been filled in the region, it is pushed onto the stack
The four-connected and eight-connected regions in step 3 are explained as follows:
Each pixel in the four connected region is connected in horizontal and vertical directions. Each pixel in the eight connected region is connected in horizontal, vertical and four diagonal directions.
conclusion
In this paper, front-end digital image processing has done a basic explanation, mainly for the acquisition of image data, image preservation, point operation, geometric processing, image enhancement, digital morphology and edge detection contour extraction to do a simple analysis and implementation, and there is no algorithm to do a deep study.
Source code address: github.com/weiruifeng/…