{"id":37074461,"url":"https://github.com/afermg/cp_measure","last_synced_at":"2026-01-14T08:46:59.424Z","repository":{"id":253716366,"uuid":"844295050","full_name":"afermg/cp_measure","owner":"afermg","description":"Morphological features from images and masks made easy. ","archived":false,"fork":false,"pushed_at":"2025-10-21T16:01:10.000Z","size":830,"stargazers_count":40,"open_issues_count":6,"forks_count":7,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-01-07T02:00:44.646Z","etag":null,"topics":["computer-vision","data-science","imaging","microscopy"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/afermg.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2024-08-19T00:26:35.000Z","updated_at":"2026-01-06T11:20:37.000Z","dependencies_parsed_at":"2024-11-13T04:25:46.220Z","dependency_job_id":"9897aeb3-2532-4347-b1b8-f9a2419e6d98","html_url":"https://github.com/afermg/cp_measure","commit_stats":null,"previous_names":["afermg/cp_measurements","afermg/cp_measure"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/afermg/cp_measure","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/afermg%2Fcp_measure","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/afermg%2Fcp_measure/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/afermg%2Fcp_measure/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/afermg%2Fcp_measure/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/afermg","download_url":"https://codeload.github.com/afermg/cp_measure/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/afermg%2Fcp_measure/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28414693,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T08:38:59.149Z","status":"ssl_error","status_checked_at":"2026-01-14T08:38:43.588Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["computer-vision","data-science","imaging","microscopy"],"created_at":"2026-01-14T08:46:58.861Z","updated_at":"2026-01-14T08:46:59.414Z","avatar_url":"https://github.com/afermg.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\u003cimg src=\"./logos/cpm.svg\" width=\"150px\"\u003e\n\u003c/div\u003e\n\n# cp_measure: Morphological features for imaging data\nDo you need to use [CellProfiler](https://github.com/CellProfiler) features, but you want to do it in a programmatic way? Look no more, this package was developed by and for the click-a-phobic scientists.\n\n## Quick overview\n\n\n### Installation\n\n```bash\npip install cp-measure\n```\n\n\n### Development\n\nIf you want to install it for development use [uv](https://docs.astral.sh/uv/).\n\n```bash\ngit clone git@github.com:afermg/cp_measure.git\ncd cp_measure\nuv sync --all-extras\n```\n\n### Usage\n\nUsers usually want to calculate all the features. There are four type of measurements, based on their inputs:\n\n-   Type 1: 1 image + 1 set of masks (e.g., intensity)\n-   Type 2: 2 images + 1 set of masks (e.g., colocalization)\n-   Type 3: 2 sets of masks (e.g., number of neighbors)\n-   Type 4: 1 image + 2 sets of masks (e.g., skeleton)\n\nThis shows the simplest way to use the first set (1 image, 1 mask set), which currently follows the style of scikit-image (1 image, 1 matrix with non-overlapping labels). **IMPORTANT:** If you need to match CellProfiler measurements 1:1, you must convert your image arrays to float values between 0 and 1. For instance, if you have an array of data type uint16, you must divide them all by 65535. This is important for radial distribution measurements.\n\nNOTE: The input labels must be sequential (e.g., `[1,2,3]`, not `[1,3,4]`). You can use `skimage.segmentation.relabel_sequential` to ensure compliance.\n\n```python\nimport numpy as np\n\nfrom cp_measure.bulk import get_core_measurements\n\nmeasurements = get_core_measurements()\nprint(measurements.keys())\n# dict_keys(['radial_distribution', 'radial_zernikes', 'intensity', 'sizeshape', 'zernike', 'ferret', 'texture', 'granularity'])\n\nimport numpy as np\nimport pandas as pd\nfrom cp_measure.bulk import get_core_measurements\n\n# Create synthetic data\nsize = 240\nrng = np.random.default_rng(42)\npixels = rng.integers(low=1, high=255, size=(size, size))\n\n# Create two similar-sized objects\nmasks = np.zeros_like(pixels)\nmasks[50:100, 50:100] = 1  # First square 50x50\nmasks[80:120, 90:120] = 1  # Major asymmetries on bottom right edge\nmasks[150:200, 150:200] = 2  # Second square 50x50\nmasks[175:180, 180:210] = 2  # Minor asymmetries on bottom right edge\n\n# Get measurements\nmeasurements = get_core_measurements()\n\nresults = {}\nfor name, v in measurements.items():\n    results = {**results, **v(masks, pixels)}\n\n\"\"\"\n{'RadialDistribution_FracAtD_1of4': array([0.03673493, 0.05640786]),\n 'RadialDistribution_MeanFrac_1of4': array([1.02857809, 1.15072037]),\n 'RadialDistribution_RadialCV_1of4': array([0.05539421, 0.04635982]),\n ...\n 'Granularity_16': array([97.65759629, 97.64371833])}\n\"\"\"\n```\n\n\n#### Call specific measurements\n\nIf you need a specific measurement/feature you can just import it. Note that measurements come in sets, so you have to fetch the one that you specifically require from the resultant dictionary. Any available measurement can be found using code as follows:\n\n```python\nimport numpy as np\n\nfrom cp_measure.minimal.measureobjectsizeshape import get_sizeshape\n\nmask = np.zeros((50, 50))\nmask[5:-6, 5:-6] = 1\nget_sizeshape(mask, None) # pixels, the second argument, is not necessary for this particular measurement\n```\n\nThe other available functions are as follows:\n\n```\nmeasureobjectintensitydistribution.get_radial_zernikes\nmeasureobjectintensity.get_intensity\nmeasureobjectsizeshape.get_zernike\nmeasureobjectsizeshape.get_ferret\nmeasuregranularity.get_granularity\nmeasuretexture.get_texture\n```\n\nAnd for Type 2 functions:\n\n```\nmeasurecolocalization.get_correlation_pearson\nmeasurecolocalization.get_correlation_manders_fold\nmeasurecolocalization.get_correlation_rwc\nmeasurecolocalization.get_correlation_costes\nmeasurecolocalization.get_correlation_overlap\n```\n  \nFor Type 3 functions:\n\n```\nmeasureobjectoverlap.measureobjectoverlap\nmeasureobjectneghbors.measureobjectneighboors\n```\n\n## Work in Progress\n\nYou can follow progress [here](https://docs.google.com/spreadsheets/d/1_7jQ8EjPwOr2MUnO5Tw56iu4Y0udAzCJEny-LQMgRGE/edit?usp=sharing).\n\n\n### Done\n\n-   Type 1 and 2 in sklearn style (multiple integer labels in one mask array)\n\n### Pending\n\n-   Add a wrapper for type 3 measurements\n-   Type 4 measurements (ObjectSkeleton). We don't know if it is worth implementing.\n\n\n# Additional notes\n\n- The Image-wide functions will not be implemented directly, they were originally implemented independently to the Object (mask) functions. We will adjust the existing functions assume that an image-wide measurement is the same as measuring an object with the same size as the intensity image.\n- This is not optimised for efficiency (yet). We aim to reproduce the 'vanilla' results of CellProfiler with minimal code changes. Optimisations will be implemented once we come up with a standard interface for functionally-focused CellProfiler components.\n- The functions exposed perform minimal checks. They will fail if provided with empty masks. Not all functions will fail if provided with masks only.\n\n\n## Similar projects\n\n- [spacr](https://github.com/EinarOlafsson/spacr): Library to analyse screens, it provides measurements (independent implementation) and a GUI.\n- [ScaleFEX](https://github.com/NYSCF/ScaleFEx): Python pipeline that includes measurements, designed for the cloud.\n- [thyme](https://github.com/tomouellette/thyme): Rust library to extract a subset of CellProfiler's features efficiently (independent implementation).\n\n## Cite\nIf you used cp\\_measure in your project, please cite using the following bib entry:\n\n```\n@article{munoz2025cp_measure,\n  title={cp\\_measure: API-first feature extraction for image-based profiling workflows},\n  author={Mu{\\~n}oz, Al{\\'a}n F and Treis, Tim and Kalinin, Alexandr A and Dasgupta, Shatavisha and Theis, Fabian and Carpenter, Anne E and Singh, Shantanu},\n  journal={arXiv preprint arXiv:2507.01163},\n  year={2025}\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fafermg%2Fcp_measure","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fafermg%2Fcp_measure","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fafermg%2Fcp_measure/lists"}