{"id":16654199,"url":"https://github.com/letmefly666/morphologicalalgorithm_particlesegmentation","last_synced_at":"2025-12-24T20:37:31.586Z","repository":{"id":38228928,"uuid":"502841395","full_name":"LetMeFly666/MorphologicalAlgorithm_ParticleSegmentation","owner":"LetMeFly666","description":"形态学算法 - 颗粒分割","archived":false,"fork":false,"pushed_at":"2022-06-13T08:34:26.000Z","size":308,"stargazers_count":6,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-12T18:26:23.079Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://maps.letmefly.xyz","language":"HTML","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/LetMeFly666.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-06-13T06:58:08.000Z","updated_at":"2024-11-10T07:57:01.000Z","dependencies_parsed_at":"2022-09-21T01:01:44.965Z","dependency_job_id":null,"html_url":"https://github.com/LetMeFly666/MorphologicalAlgorithm_ParticleSegmentation","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/LetMeFly666/MorphologicalAlgorithm_ParticleSegmentation","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LetMeFly666%2FMorphologicalAlgorithm_ParticleSegmentation","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LetMeFly666%2FMorphologicalAlgorithm_ParticleSegmentation/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LetMeFly666%2FMorphologicalAlgorithm_ParticleSegmentation/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LetMeFly666%2FMorphologicalAlgorithm_ParticleSegmentation/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/LetMeFly666","download_url":"https://codeload.github.com/LetMeFly666/MorphologicalAlgorithm_ParticleSegmentation/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LetMeFly666%2FMorphologicalAlgorithm_ParticleSegmentation/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28008431,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-12-24T02:00:07.193Z","response_time":83,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":[],"created_at":"2024-10-12T09:49:01.543Z","updated_at":"2025-12-24T20:37:31.572Z","avatar_url":"https://github.com/LetMeFly666.png","language":"HTML","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003c!--\n * @Author: LetMeFly\n * @Date: 2022-06-13 15:00:00\n * @LastEditors: LetMeFly\n * @LastEditTime: 2022-06-13 16:08:18\n--\u003e\n# MorphologicalAlgorithm_ParticleSegmentation\n\n形态学算法 - 颗粒分割\n\n项目地址：[https://github.com/LetMeFly666/MorphologicalAlgorithm_ParticleSegmentation](https://github.com/LetMeFly666/MorphologicalAlgorithm_ParticleSegmentation)\n\n在线文档： [https://maps.letmefly.xyz](https://maps.letmefly.xyz/)\n\n## 问题描述\n\n显微应用中一个预处理步骤是从两组或更多组重叠的类似颗粒（见右图）中分离出单个独立的一种颗粒。假设所有颗粒的大小相同，提出一种产生3幅图像的形态学算法，这3幅图像分别仅由如下物体组成：\n\n+ (a) 仅与图像边界融合在一起的颗粒\n+ (b) 仅彼此重叠的颗粒\n+ (c) 没有重叠的颗粒\n\n\n![9.36.jpg](img/9.36.jpg#center)\n\n## 具体方法：\n\n拿到图像后，首先进行二值化处理，然后进行一个开运算。\n\n本项目用Python实现。到此为止，图像(numpy.array)中的数据只有0和255\n\n由此我们可以使用opencv中自带的连通块划分函数，将不同的颗粒(块)划分为不同的连通块（记为“标签图片”）。\n\n比如：\n\n```\n0000000000\n0011000200\n0010022203\n0000022003\n0000000000\n```\n\n+ 0是黑色的区域\n+ 1是一个白色的颗粒（面积比较小，只有3，因此应该只有单个颗粒）\n+ 2是一个白色的颗粒块（面积比较大，有6，因此推测有多个颗粒重叠到了一起）\n\n然后，我们就可以根据划分出来的连通块，来区分不同类型的颗粒了。\n\n### 与边界重合的颗粒\n\n我们只需要遍历一下4个边界。如果一个颗粒与边界重合(边界上出现了不为0的标签)，那么我们就记录下这个颗粒的标签。\n\n例如上面小图的3。\n\n之后遍历一遍图像，并新建一个全为0的大小相同的图像（记为“边界图像”），找到标签图片中是“3”的位置，把边界图像中对应位置记为255。\n\n这样，我们就提取出了**边界颗粒**。\n\n### 相互重叠的颗粒\n\n提取出了边界颗粒后，我们用开运算后的图像减去边界图像，只研究未分类的剩余颗粒。\n\n假设一个颗粒的面积大约为3，那么我们只需要统计每个种类的标签的像素个数，＞3的就视为是**有重叠的颗粒**。\n\n### 单个的颗粒\n\n提取出了相互重叠的颗粒后，只需要拿剩余颗粒减去有重叠的颗粒，就能得到**单独的颗粒**。\n\n## 代码实现\n\n```Python\n'''\nAuthor: LetMeFly\nDate: 2022-06-12 23:28:36\nLastEditors: LetMeFly\nLastEditTime: 2022-06-13 15:01:56\n'''\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport os\nimport cv2\n\n# 避免plt警告\nos.environ[\"QT_DEVICE_PIXEL_RATIO\"] = \"0\"\nos.environ[\"QT_AUTO_SCREEN_SCALE_FACTOR\"] = \"1\"\nos.environ[\"QT_SCREEN_SCALE_FACTORS\"] = \"1\"\nos.environ[\"QT_SCALE_FACTOR\"] = \"1\"\n\n# 支持中文显示\nplt.rcParams[ 'font.sans-serif' ] = [ 'SimHei' ]\n\n# 读入图像\nimg = cv2.imread(\"img/9.36.jpg\", 0)\nrows, cols = img.shape\n\n# 二值化\n_, img_binary = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)\nimg_open = cv2.morphologyEx(img_binary, cv2.MORPH_OPEN, kernel=cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3)))  # 开运算\nimg_open_backup = img_open.copy()\n\n# 把图像分成一个个连通块儿\nnum, img_label = cv2.connectedComponents(img_open)\nlabels = [i for i in range(1, num)]\n\nbounded_labels = set()  # 处在边界的标签\nfor col in range(cols):\n    if img_label[0][col]:\n        bounded_labels.add(img_label[0][col])\n    if img_label[rows - 1][col]:\n        bounded_labels.add(img_label[rows - 1][col])\nfor row in range(rows):\n    if img_label[row][0]:\n        bounded_labels.add(img_label[row][0])\n    if img_label[row][cols - 1]:\n        bounded_labels.add(img_label[row][cols - 1])\n\n# 与边界重合的部分\nimg_bounds = np.zeros((rows, cols), dtype=np.uint8)\nfor row in range(rows):\n    for col in range(cols):\n        if img_label[row][col] in bounded_labels:\n            img_bounds[row][col] = 255\nimg_open -= img_bounds\n\n# 获取各个标签的面积\narea_dict = {}\nfor label in labels:\n    area_dict[label] = 0\nfor row in range(rows):\n    for col in range(cols):\n        if img_open[row][col]:\n            area_dict[img_label[row][col]] += 1\n\n# 设置单个颗粒的面积阈值\nsingle_area = 420  # 经过调试，420是个不错的选择\n\n# 相互重叠的图像（面积 \u003e 单个颗粒的图像）\nimg_overlap = np.zeros((rows, cols), np.uint8)\nfor row in range(rows):\n    for col in range(cols):\n        if img_label[row][col] and area_dict[img_label[row][col]] \u003e single_area:\n            img_overlap[row][col] = 255\n\n# 剩下的就是单个颗粒的部分\nimg_single = img_open - img_overlap\n\n# 显示结果\n_, ax_list = plt.subplots(1, 5, figsize=(20, 10))\nax_list[0].set_title(\"原图\")\nax_list[0].imshow(img, cmap=\"gray\")\nax_list[1].set_title(\"开运算\")\nax_list[1].imshow(img_open_backup, cmap=\"gray\")\nax_list[2].set_title(\"与边界融合\")\nax_list[2].imshow(img_bounds, cmap=\"gray\")\nax_list[3].set_title(\"相互重叠\")\nax_list[3].imshow(img_overlap, cmap=\"gray\")\nax_list[4].set_title(\"没有重叠\")\nax_list[4].imshow(img_single, cmap=\"gray\")\nplt.show()\n```\n\n## 实现结果\n\n![result.jpg](img/result.jpg)\n\n\u003e 同步发文于CSDN，原创不易，转载请附上[原文链接](https://maps.letmefly.xyz)哦~\n\u003e Tisfy：[https://letmefly.blog.csdn.net/article/details/125258729](https://letmefly.blog.csdn.net/article/details/125258729)","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fletmefly666%2Fmorphologicalalgorithm_particlesegmentation","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fletmefly666%2Fmorphologicalalgorithm_particlesegmentation","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fletmefly666%2Fmorphologicalalgorithm_particlesegmentation/lists"}