overlay

package
v0.0.0-...-0d45792 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 26, 2024 License: BSD-3-Clause Imports: 25 Imported by: 1

README

Counting connected components with the overlay library

This README provides demonstration code, documenting how to use this library to count connected components within an annotation PNG file.


package main

import (
	"fmt"
	"log"

	"github.com/carbocation/genomisc/overlay"
)

func main() {

	// Config.json file that comports with the type definition within
	// json-config.go
	configPath := "./demo.config.json"

	// Your annotation PNG. Based on the config file, pixels contributing to
	// background should be colored #000000, pixels contributing to ascending
	// aorta #010101, and pixels contributing to descending aorta #020202. No
	// other colors should exist in this image.
	filePath := "./annotation.png"

	// The connected components tool performs a secondary count that ignores
	// connected components that are built from of a small number of pixels
	// below this cutoff. This allows it to ignore small errors, such as an
	// errant, noisy pixel. The units for this value are "pixels". Here,
	// clusters with fewer than 5 pixels are ignored.
	threshold := 5

	// Those are the configurable parameters. Subsequent lines open the image
	// and your config file and apply the connected components algorithm.

	config, err := overlay.ParseJSONConfigFromPath(configPath)
	if err != nil {
		log.Fatalln(err)
	}

	rawOverlayImg, err := overlay.OpenImageFromLocalFile(filePath)
	if err != nil {
		log.Fatalln(err)
	}

	// Generate the function to count connected components within your
	// annotation file
	connected, err := overlay.NewConnected(rawOverlayImg)
	if err != nil {
		log.Fatalln(err)
	}

	// Perform the connected component count
	_, connectedCounts, _, thresholdedConnectedCounts, err := connected.Count(config.Labels, threshold)
	if err != nil {
		log.Fatalln(err)
	}

	// Report the results
	for label := range connectedCounts {
		fmt.Printf("Connected components for %s: %d\n", label.Label, connectedCounts[label])
		fmt.Printf("Connected components for %s, thresholded at %d pixels: %d\n", label.Label, threshold, thresholdedConnectedCounts[label])
	}
}

Documentation

Index

Constants

View Source
const (
	WhichPointBottomRight = "br"
	WhichPointTopLeft     = "tl"
)
View Source
const SpecialTransparentColor = "#000001"

SpecialTransparentColor is a color which will render as transparent.

Variables

This section is empty.

Functions

func DilateDimension

func DilateDimension(pos, max, dilationFactor int, direction string) int

DilateDimension expands an axis by "dilationFactor" pixels (additive). It basically adds or subtracts pixels, while paying attention to not allow the lower bound of the image to go below 0.

func ImageFromBytes

func ImageFromBytes(imgBytes []byte) (image.Image, error)

ImageFromBytes creates an image from the specified bytes. Must be PNG, GIF, BMP, or JPEG formatted (based on the decoders we have imported).

func LabeledPixelToID

func LabeledPixelToID(c color.Color) (uint32, error)

LabeledPixelToID converts the label-encoded pixel (e.g., #010101) which is alpha-premultiplied into an ID in the range of 0-255

func OpenImageFromLocalFile

func OpenImageFromLocalFile(filePath string) (image.Image, error)

OpenImageFromLocalFile pulls an image with the specified suffix (derived from the DICOM name) from a local folder

func OpenImageFromLocalFileOrGoogleStorage

func OpenImageFromLocalFileOrGoogleStorage(filePath string, storageClient *storage.Client) (image.Image, error)

func SubsetAndRescaleImage

func SubsetAndRescaleImage(baseImg image.Image, topLeftX, topLeftY, bottomRightX, bottomRightY, scale, dilation int) (image.Image, error)

SubsetAndRescaleImage takes an input image, crops based on a top left point and a bottom right point, dilates, and then rescales the image.

Types

type CentralMoments

type CentralMoments struct {
	Bounds struct {
		TopLeft     Coord
		BottomRight Coord
	}
	Area     float64
	Centroid struct {
		X, Y float64
	}
	LongAxisOrientationRadians float64
	LongAxisPixels             float64
	ShortAxisPixels            float64
	Eccentricity               float64
}

type Connected

type Connected struct {
	// Each point represents a label ID
	PixelLabelIDs [][]uint8

	// Each point represents a connected component ID
	PixelConnectedComponentIDs [][]uint32

	// Each component of each label is tracked, including bounding box and pixel count
	LabeledConnectedComponents map[uint8]map[uint32]ConnectedComponent
}

func NewConnected

func NewConnected(img image.Image) (*Connected, error)

func (*Connected) ComputeMoments

func (c *Connected) ComputeMoments(component ConnectedComponent, method MomentMethod) (CentralMoments, error)

func (*Connected) Count

func (c *Connected) Count(l LabelMap, threshold int) (rawPixels, rawCounts, thresholdedPixels, thresholdedCounts map[Label]int, err error)

Count evaluates the number of pixels for each label, the number of components for each label, and then also performs the same operations on a subset of the data that requires that a threshold be met (in terms of number of pixels within a contiguous component) in order to be counted. This permits you to, e.g., ignore single-pixel noisy blips that appear and which shouldn't be counted towards area measurements.

func (Connected) LabelAbove

func (c Connected) LabelAbove(x, y int) (bool, uint32)

func (Connected) LabelLeft

func (c Connected) LabelLeft(x, y int) (bool, uint32)

type ConnectedComponent

type ConnectedComponent struct {
	LabelID     uint8
	ComponentID uint32
	PixelCount  int
	Bounds      struct {
		TopLeft     Coord
		BottomRight Coord
	}
}

func MergeConnectedComponentsSameLabel

func MergeConnectedComponentsSameLabel(c1, c2 ConnectedComponent) ConnectedComponent

MergeConnectedComponents brings two ConnectedComponents together. In doing so, it assumes that the LabelID is the same between the two (and sets it to the LabelID of the first, regardless). It unsets the component ID - this merged value is now only useful in the context of doing a summary level evaluation on the whole label.

type Coord

type Coord struct {
	X, Y int
}

type JSONConfig

type JSONConfig struct {
	ConfigPath   string
	ManifestPath string   `json:"manifest"`
	Port         int      `json:"port"`
	Labels       LabelMap `json:"labels"`
	LabelPath    string   `json:"label_path"`
	ImagePath    string   `json:"image_path"`
	ImageSuffix  string   `json:"image_suffix"`
	DefaultBrush string   `json:"default_brush"`
	PreParsed    bool     `json:"preparsed"`
	BrushSize    int      `json:"brush_size,omitempty"`
}

func ParseJSONConfigFromPath

func ParseJSONConfigFromPath(path string) (JSONConfig, error)

type Label

type Label struct {
	Label     string
	ID        uint    `json:"id"`
	Color     string  `json:"color"`
	SortOrder float64 `json:"sort_order,omitempty"`
}

A Label tracks the segmentation ID with the human-identifiable Label and human-interpretable color (in RGB hex, e.g., #FF0000 for red).

type LabelMap

type LabelMap map[string]Label

LabelMap ([string label name]Label) keeps track of the relationship between human-visible colors and the segmentation ID (used for deep learning) of that label.

func (LabelMap) DecodeImageFromImageSegment

func (l LabelMap) DecodeImageFromImageSegment(bmpImage image.Image, transparentBackground bool) (image.Image, error)

DecodeImageFromImageSegment consumes an ID-encoded image (where each pixel is #010101 for ID 1, #020202 for ID 2 etc), and transforms it into a human-visible value based on the colors for those IDs assigned in the config file. It special-cases ID 0 (the background) and color value #000001 to be transparent. However, if transparentBackground is false, then the background is set to black.

func (LabelMap) DecodeImageFromRLE

func (l LabelMap) DecodeImageFromRLE(rleBytes []byte, maxX, maxY int) (image.Image, error)

func (LabelMap) EncodeImageToImageSegment

func (l LabelMap) EncodeImageToImageSegment(bmpImage image.Image) (image.Image, error)

EncodeImageToImageSegment consumes a multi-color human-visible image into an image where each pixel has the same R, G, and B value mapped to the integer ID of the Label. For example, if background is transparent (ID 0) and the left atrium is red (ID 1), it will produce an image that is all black, with values #000000 for the background and #010101 for the left atrium.

func (LabelMap) EncodeImageToRLE

func (l LabelMap) EncodeImageToRLE(bmpImage image.Image) ([]byte, error)

func (LabelMap) Sorted

func (l LabelMap) Sorted() []Label

Sorted permits sorting by arbitrary criteria - may be useful for reordering labels for different blocks of segmentation. Internally, all mapping is done based on the explicitly set ID, so the sort order does not matter (except perhaps for performance in for loops).

func (LabelMap) ToIDMap

func (l LabelMap) ToIDMap() map[uint]Label

func (LabelMap) Valid

func (l LabelMap) Valid() bool

Valid ensures that the LabelMap is valid by testing that it is bijective. If not, it's invalid. (Previously checked to make sure that IDs started at 0 and had no gaps, but that requirement has since been relaxed.)

type MomentMethod

type MomentMethod uint8
const (
	MomentMethodConnected MomentMethod = iota
	MomentMethodLabel
)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL