1. Background
Recently, there is a need to extract and convert the words spliced by the small squares of the picture into a hexadecimal byte array, and then store them in the font library for use by single-chip microcomputer devices. Font in three formats (width x height): 12×16, 16×16 and 24×24. There are a lot of fonts and pictures in this font, so it is more eye-catching to identify and convert with the naked eye, so I decided to use the method of picture recognition to automatically recognize the picture and convert it into a byte array. But I couldn’t find a suitable solution on the Internet, so I finally realized it by myself.
For example, this picture:
2. Ideas
1. Binarize the picture, convert the pixels of the picture into a two-dimensional array composed of 0 and 1;
2. Convert the binarized array into a new binary array (array of squares) according to the size of the squares, where the black squares are 1 and the other squares are 0;
3. The block array array can be converted into the corresponding byte array according to the specified format.
Note: The new binary array is converted according to every 8 points in the vertical direction as a byte.
3. Results
Look at the result first, then there is code implementation
1. Hex byte array
power-24x24={0x00,0x00,0xF0,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0xFE,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0xF0,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0xFF,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0xFF,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0xFF,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x3F,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x40,0x20,0x1C,0x00, 0x00}
2. The words printed on the console
................................ ..........#.......... ..........#.......... ..........#.......... ..##################..... ..#.......#.......#..... ..#.......#.......#..... ..#.......#.......#..... ..#.......#.......#..... ..#.......#.......#..... ..##################..... ..#.......#.......#..... ..#.......#.......#..... ..#.......#.......#..... ..#.......#.......#..... ..#.......#.......#..... ..##################..... ..........#.......... ..........#..........#.. ..........#..........#.. ..........#..........#.. ..........#..........#... ..........#########.... ...................................
4. Specific code implementation
1. Font converter
import lombok.extern.slf4j.Slf4j; import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; /** * PictureFontConvertor * * @author hxj * @date 2023-03-22 11:00 */ @Slf4j public class PictureFontConvertor { /** * Positive integer corresponding to 8 bits in an eight-bit binary integer */ static int[] bitNum = {1,2,4,8,16,32,64,128}; private String name; BufferedImage bufImage; /** * Picture size - wide */ private int picWidth; /** * Picture size-high */ private int picHeight; /** * Image pixel point two-dimensional array arrangement */ private int[][] pixels; /** * Square two-dimensional array arrangement */ public int[][] squareMap; /** * Horizontal number of blocks */ private int squareWidthNum; /** * The vertical number of blocks */ private int squareHeightNum; public PictureFontConvertor(String picPath, int squareWidthNum, int squareHeightNum) throws IOException { File file = new File(picPath); String fileName = file.getName().substring(0, file.getName().lastIndexOf(".")); bufImage = ImageIO. read(file); this.squareWidthNum = squareWidthNum; this.squareHeightNum = squareHeightNum; picWidth = bufImage.getWidth();//The width of the picture picHeight = bufImage.getHeight();//The height of the picture pixels = new int[picHeight][picWidth]; squareMap = new int[squareHeightNum][squareWidthNum]; name = String.format("%s-%dx%d",fileName, squareWidthNum, squareHeightNum); } public String getName(){ return name; } /** * Convert the light-colored squares in the figure to 0, and the black squares to 1, and record them in a two-dimensional array in order */ public void convertSquare() { binaryImage(); calculateSquareMap(); } /** * Converted to a hexadecimal string of unsigned 8-bit integers * @return */ public String toUInit8HexStr() { return bytesToHexStr(toByteArray()); } /** * Convert to byte array * @return */ public byte[] toByteArray() { int y = squareHeightNum/8; int x = squareWidthNum; byte[] items = new byte[y*x]; for (int h = 0; h < y; h ++ ) { for (int l = 0; l < x; l ++ ) { int n = 0; for (int i = 0; i < 8; i ++ ) { if (squareMap[h*8 + i][l] == 1) { n + = bitNum[i]; } } items[(h*x) + l] = (byte) n; } } return items; } /** * Print out the converted result * @param blackPoint The character replaced by the black square * @param whitePoint The replacement character for other squares */ public void printSquareMap(String blackPoint, String whitePoint) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < squareMap. length; i ++ ) { int[] items = squareMap[i]; for (int item : items) { if (item == 1) { sb.append(blackPoint); } else { sb.append(whitePoint); } } sb.append("\\ "); } System.out.println(sb); } /** * Generate an array of squares */ private void calculateSquareMap() { // block size int size = picWidth/squareWidthNum; // Extra horizontal pixels int yushuX = picWidth - squareWidthNum * size; // Extra vertical pixels int yushuY = picHeight - squareHeightNum * size; // Cumulative offset in horizontal and vertical directions int xOffset = 0; int yOffset = 0; for (int y = 1; y <= squareHeightNum; y++ ) { xOffset=0; if (y <= yushuY) { yOffset++; } int ybu = y<=yushuY? 1:0; int startY = yOffset == 0 ? (y-1)*size : (y-1)*size + yOffset-1; int endY = startY + size + ybu; for (int x = 1; x <= squareWidthNum; x ++ ) { if (x <= yushuX) { xOffset++; } int xbu = x<=yushuX? 1:0; int startX = xOffset ==0? (x-1)*size : (x-1)*size + xOffset-1; int endX = startX + size + xbu; /* If the proportion of the black area exceeds 75, it is a black square. * Because there is a certain offset when taking the square pixel points, part of the taken out points is a white point, so the judgment is made according to the ratio. */ int ratio = countBlackRatio(startX, endX, startY, endY); if (ratio > 75) { squareMap[y-1][x-1] = 1; } else { squareMap[y-1][x-1] = 0; } } } } /** * Calculate the proportion of the black area in the square * @param startX * @param endX * @param startY * @param endY * @return */ private int countBlackRatio(int startX, int endX, int startY, int endY) { int total=0; int black=0; for (int y = startY; y < endY; y++ ) { for (int x = startX; x < endX; x ++ ) { total + + ; try { black + = pixels[y][x]; } catch (Exception e) { log.info("Calculation of black dot ratio is abnormal: x={}, y={}", x, y); throw new RuntimeException(e); } } } return black*100/total; } /** * Binarized image pixels */ public void binaryImage(){ int minX = 0;//Image starting point X int minY = 0;//The starting point of the picture Y //Reduce the black area to 1, and the others to 0 for (int i = minX; i < picWidth; i ++ ) { for (int j = minY; j < picHeight; j++ ) { Object data = bufImage.getRaster().getDataElements(i, j, null);//Get the pixel of this point and express it in object type int red = bufImage.getColorModel().getRed(data); int blue = bufImage.getColorModel().getBlue(data); int green = bufImage.getColorModel().getGreen(data); if(red==0 & amp; & amp;green==0 & amp; & amp;blue==0){ pixels[j + 1 ][i + 1 ] = 1; } } } } /** * Byte array to hexadecimal string * @param bytes * @return */ private static String bytesToHexStr(byte[] bytes) { StringBuilder sb = new StringBuilder(); sb.append("{"); for (int i=0; i<bytes. length; i + + ) { int low = bytes[i] & 0x0F; int high = (bytes[i] >> 4) & 0x0F; sb.append("0x"); sb.append(byteToChar(high)); sb.append(byteToChar(low)); if (i<bytes. length-1) { sb.append(","); } } sb.append("}"); return sb.toString(); } public static char byteToChar(int b) { char ch = (b < 0xA) ? (char) ('0' + b) : (char) ('A' + b - 10); return ch; } }
3. Generate results
public static void main(String[] args) throws IOException { String filePath = "./doc/.png"; PictureFontConvertor readPictureSquare = new PictureFontConvertor(filePath, 24,24); readPictureSquare. convertSquare(); readPictureSquare.printSquareMap("#","."); String str = readPictureSquare.toUInit8HexStr(); System.out.println(String.format("%s=%s",readPictureSquare.getName(), str)); }
2. Result test
/** * FontBytesCheck * * @author hxj * @date 2023-03-21 13:40 */ public class FontBytesCheck { static int[] bs = {1,2,4,8,16,32,64,128}; public static void printFont(int[] bytes, int with, int height) { int charHeight = height/8; for (int i = 1; i <= charHeight; i ++ ) { for (int ci = 0; ci < 8; ci ++ ) { for (int j = 0; j < with; j ++ ) { if ((bytes[(i-1)*with + j] & bs[ci]) > 0 ) { System.out.print("#"); } else { System.out.print(" "); } if (j==with-1) { System.out.println(); } } } } } public static void main(String[] args) { int[] bytes = {0x00,0x00,0xF0,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0xFE,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0xF0,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0xFF,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0xFF,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0xFF,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x3F,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x40,0x20,0x1C,0x00,0x00} ; printFont(bytes, 24, 24); }