这是 COCO RLE 掩码的示例 - https://pastebin.com/ZhE2en4C
这是 YOLOv8 验证运行的输出,取自生成的 Predictions.json 文件。
我正在尝试在 JavaScript 中解码该字符串并将其呈现在画布上。编码的字符串是有效的,因为在 python 中我可以这样做:
from pycocotools import mask as coco_mask from PIL import Image example_prediction = { "image_id": "102_jpg", "category_id": 0, "bbox": [153.106, 281.433, 302.518, 130.737], "score": 0.8483, "segmentation": { "size": [640, 640], "counts": "<RLE string here>" } } def rle_to_bitmap(rle): bitmap = coco_mask.decode(rle) return bitmap def show_bitmap(bitmap): img = Image.fromarray(bitmap.astype(np.uint8) * 255, mode='L') img.show() input("Press Enter to continue...") img.close() mask_bitmap = rle_to_bitmap(example_prediction["segmentation"]) show_bitmap(mask_bitmap)
我可以看到解码后的掩码。
是否有一个库可以用来解码 JavaScript 中的相同字符串并将其转换为 Image
?我尝试深入研究 pycocotools 的源代码,但我做不到。
P粉0249861502023-12-08 09:11:58
您可以在画布上绘制蒙版,然后根据需要导出图像。
对于实际绘图,您可以使用两种方法:
以下是两者的示例:
// Styling and scaling just for demo let wrapper = document.createElement("div") wrapper.style.cssText = ` transform-origin: left top; transform: scale(8); ` document.body.style.cssText = ` background-color: #121212; margin: 0; overflow: hidden; ` document.body.appendChild(wrapper) // Helpers function createCanvas(width, height) { let canvas = document.createElement("canvas") canvas.style.cssText = ` border: 1px solid white; display: block; float: left; image-rendering: pixelated; ` canvas.height = height canvas.width = width // Comment this line if you need only image sources wrapper.appendChild(canvas) return canvas } function randomColorRGBA() { return [ Math.round(Math.random() * 255), Math.round(Math.random() * 255), Math.round(Math.random() * 255), 255 ] } // Fast array flattening (faster than Array.proto.flat()) function flatten(arr) { const flattened = [] !(function flat(arr) { arr.forEach((el) => { if (Array.isArray(el)) flat(el) else flattened.push(el) }) })(arr) return flattened } // Decode from RLE to Binary Mask // (pass false to flat argument if you need 2d matrix output) function decodeCocoRLE([rows, cols], counts, flat = true) { let pixelPosition = 0, binaryMask if (flat) { binaryMask = Array(rows * cols).fill(0) } else { binaryMask = Array.from({length: rows}, (_) => Array(cols).fill(0)) } for (let i = 0, rleLength = counts.length; i < rleLength; i += 2) { let zeros = counts[i], ones = counts[i + 1] ?? 0 pixelPosition += zeros while (ones > 0) { const rowIndex = pixelPosition % rows, colIndex = (pixelPosition - rowIndex) / rows if (flat) { const arrayIndex = rowIndex * cols + colIndex binaryMask[arrayIndex] = 1 } else { binaryMask[rowIndex][colIndex] = 1 } pixelPosition++ ones-- } } if (!flat) { console.log("Result matrix:") binaryMask.forEach((row, i) => console.log(row.join(" "), `- row ${i}`)) } return binaryMask } // 1. Draw from binary mask function drawFromBinaryMask({size, counts}) { let fillColor = randomColorRGBA(), height = size[0], width = size[1] let canvas = createCanvas(width, height), canvasCtx = canvas.getContext("2d"), imgData = canvasCtx.getImageData(0, 0, width, height), pixelData = imgData.data // If you need matrix output (flat = false) // let maskFlattened = flatten(decodeCocoRLE(size, counts, false)), // maskLength = maskFlattened.length; // If not - it's better to use faster approach let maskFlattened = decodeCocoRLE(size, counts), maskLength = maskFlattened.length; for(let i = 0; i < maskLength; i++) { if (maskFlattened[i] === 1) { let pixelPosition = i * 4 pixelData[pixelPosition] = fillColor[0] pixelData[pixelPosition + 1] = fillColor[1] pixelData[pixelPosition + 2] = fillColor[2] pixelData[pixelPosition + 3] = fillColor[3] } } canvasCtx.putImageData(imgData, 0, 0) // If needed you can return data:image/png // to use it as an image.src return canvas.toDataURL() } // 2. Draw using virtual canvas function drawDirectlyFromRle({size: [rows, cols], counts}) { let fillColor = randomColorRGBA(), isOnesInterval = false, start = 0, end = 0 let realCanvas = createCanvas(cols, rows), realCtx = realCanvas.getContext("2d") let virtualCanvas = new OffscreenCanvas(rows, cols), virtualCtx = virtualCanvas.getContext("2d"), imgData = virtualCtx.getImageData(0, 0, rows, cols), pixelData = imgData.data counts.forEach((interval) => { end = start + interval * 4 if (isOnesInterval) { for (let i = start; i < end; i += 4) { pixelData[i] = fillColor[0] pixelData[i + 1] = fillColor[1] pixelData[i + 2] = fillColor[2] pixelData[i + 3] = fillColor[3] } } start = end isOnesInterval = !isOnesInterval }) virtualCtx.putImageData(imgData, 0, 0) realCtx.save() realCtx.scale(-1, 1) realCtx.rotate(90*Math.PI/180) realCtx.drawImage(virtualCanvas, 0, 0) realCtx.restore() // If needed you can return data:image/png // to use it as an image.src return realCanvas.toDataURL() } // Test RLE const exampleCocoRLE = { counts: [15, 1, 9, 1, 3, 3, 2, 1, 8, 1, 8, 1, 3, 3, 2, 1, 8, 1, 7, 1, 11], size: [9, 10] } // Draw on canvas let imageSrc1 = drawFromBinaryMask(exampleCocoRLE), imageSrc2 = drawDirectlyFromRle(exampleCocoRLE) console.log("Canvas 1 image (from binary):\n", imageSrc1) console.log("Canvas 2 image (from virtual):\n", imageSrc2) // Example of src usage let image1 = document.createElement("img"), image2 = document.createElement("img"), imageStyle = ` display: block; float: left; border: 1px solid lime; image-rendering: pixelated; ` // demo styling image1.style.cssText = imageStyle image2.style.cssText = imageStyle image1.onload = () => { wrapper.appendChild(image1) } image2.onload = () => { wrapper.appendChild(image2) } image1.src = imageSrc1 image2.src = imageSrc2