如何将一个1通道数组保存为具有适当颜色限制的图像

huangapple go评论104阅读模式
英文:

How to save a 1-channel array as an image with proper color limit

问题

I am trying to save a sequence of 1-channel array into grayscale images. These images are supposed to be masks for segmentation.

The issue I am facing is inconsistent colors for the same pixel value in different images:

Image #1
如何将一个1通道数组保存为具有适当颜色限制的图像

When I add additional circles to this image, then the saved image has a different set of colors for previously present circles.

Image #2
如何将一个1通道数组保存为具有适当颜色限制的图像

Ideally, I want them to have consistent colors throughout my entire dataset as each circle represents a unique class of label.

So my question is how can I save these grayscale images with consistent color code?

Added code snippet to generate the above image

  1. import numpy as np
  2. import cv2
  3. import matplotlib.image as mpimg
  4. image = np.zeros(shape=[256, 256], dtype=np.uint8)
  5. cv2.circle(image, center=(50, 50), radius=10, color=(2, 2), thickness= -1)
  6. cv2.circle(image, center=(100, 100), radius=10, color=(3, 3), thickness= -1)
  7. cv2.circle(image, center=(150, 150), radius=10, color=(4, 4), thickness= -1)
  8. cv2.circle(image, center=(75, 200), radius=10, color=(5, 5), thickness= -1)
  9. cv2.circle(image, center=(200, 89), radius=10, color=(6, 6), thickness= -1)
  10. # additional circles (they are part of Image #2)
  11. cv2.circle(image, center=(21, 230), radius=5, color=(7, 7), thickness= -1)
  12. cv2.circle(image, center=(149, 250), radius=5, color=(12, 12), thickness= -1)
  13. mpimg.imsave('image.jpg',image)
英文:

I am trying to save a sequence of 1-channel array into grayscale images. These images are supposed to be masks for segmentation.

The issue I am facing is inconsistent colors for same pixel value in different images:

Image #1
如何将一个1通道数组保存为具有适当颜色限制的图像

When I add additional circles to this image, then saved image has different set of colors for previous present circles.

Image #2
如何将一个1通道数组保存为具有适当颜色限制的图像

Ideally I want them to have consistent colors throughout my entire dataset as each circle represent a unique class of label.

So my question is how can I save these grayscale images with consistent color code?

Added code snippet to generate above image

  1. import numpy as np
  2. import cv2
  3. import matplotlib.image as mpimg
  4. image = np.zeros(shape=[256, 256], dtype=np.uint8)
  5. cv2.circle(image, center=(50, 50), radius=10, color=(2, 2), thickness= -1)
  6. cv2.circle(image, center=(100, 100), radius=10, color=(3, 3), thickness= -1)
  7. cv2.circle(image, center=(150, 150), radius=10, color=(4, 4), thickness= -1)
  8. cv2.circle(image, center=(75, 200), radius=10, color=(5, 5), thickness= -1)
  9. cv2.circle(image, center=(200, 89), radius=10, color=(6, 6), thickness= -1)
  10. # additional circles (they are part of Image #2)
  11. cv2.circle(image, center=(21, 230), radius=5, color=(7, 7), thickness= -1)
  12. cv2.circle(image, center=(149, 250), radius=5, color=(12, 12), thickness= -1)
  13. mpimg.imsave('image.jpg',image)

答案1

得分: 3

以下是翻译好的部分:

首先一些提示...

  • 你不能将JPEG格式用于分类数据。JPEG是一种用于照片的有损格式,允许更改像素以使其在重新阅读时“看起来相似”,但实际上会有所不同。请使用无损PNG格式用于类数据。请参阅答案末尾的演示。

  • 每个像素位置都用单个数字表示你的类别,这意味着你需要单通道图像,也就是灰度图像。这意味着在绘制圆圈时,应该将颜色指定为单个数字,而不是2个数字的元组。

  • 没有必要引入不必要的依赖项 - 你已经在使用OpenCV,所以读取和写入图像也应使用它。

这意味着你的代码应该更像这样:

  1. #!/usr/bin/env python3
  2. import numpy as np
  3. import cv2 as cv
  4. image = np.zeros(shape=[256, 256], dtype=np.uint8)
  5. cv.circle(image, center=(50, 50), radius=10, color=2, thickness= -1)
  6. cv.circle(image, center=(100, 100), radius=10, color=3, thickness= -1)
  7. cv.circle(image, center=(150, 150), radius=10, color=4, thickness= -1)
  8. cv.circle(image, center=(75, 200), radius=10, color=5, thickness= -1)
  9. cv.circle(image, center=(200, 89), radius=10, color=6, thickness= -1)
  10. # additional circles (they are part of Image #2)
  11. cv.circle(image, center=(21, 230), radius=5, color=7, thickness= -1)
  12. cv.circle(image, center=(149, 250), radius=5, color=12, thickness= -1)
  13. cv.imwrite('classes.png', image)

这将以非常低的对比度保存你的图像 - 所有像素将在0..255的可能范围内的0..12范围内,因此它们将很暗,但仍然包含你需要的信息。

要进行可视化,你需要进行对比度拉伸或将数据归一化到整个范围。你可以使用ImageMagick来实现:

  1. magick classes.png -normalize classes-norm.png

如果你不想使用ImageMagick进行归一化,可以在OpenCV中进行等效操作:

  1. normalizedImg = cv.normalize(img, None, 0, 255, cv.NORM_MINMAX)

如果你想将自己的颜色映射应用于数据,可以使用LUT "Lookup Table" 来实现,如下所示:

  1. #!/usr/bin/env python3
  2. import sys
  3. import cv2 as cv
  4. import numpy as np
  5. if __name__ == "__main__":
  6. # 检查提供的文件名
  7. if len(sys.argv) != 2:
  8. sys.exit('Usage: argv[0] filename')
  9. # 读取灰度类图像
  10. image = cv.imread(sys.argv[1], cv.IMREAD_GRAYSCALE)
  11. # 创建具有3个(RGB)条目的256个条目的调色板,最初看起来像灰度
  12. pal = np.fromfunction(lambda i,j: i, (256,3), dtype=np.uint8)
  13. # 覆盖从灰色到新颜色的任何所需颜色
  14. pal[0] = [0,0,0] # 0 映射到黑色
  15. pal[1] = [255,0,0] # 1 映射到红色
  16. pal[2] = [0,255,0] # 2 映射到绿色
  17. pal[3] = [0,0,255] # 3 映射到蓝色
  18. pal[4] = [255,255,0] # 4 映射到黄色
  19. pal[5] = [0,255,255] # 5 映射到青色
  20. pal[6] = [255,0,255] # 6 映射到洋红
  21. pal[7] = [128,0,0] # 7 映射到深红
  22. pal[8] = [0,128,0] # 8 映射到深绿
  23. pal[9] = [0,0,128] # 9 映射到深蓝
  24. # 转换LUT为OpenCV的BGR顺序
  25. pal = pal[:,::-1]
  26. # 在LUT中查找每个像素
  27. result = pal[image]
  28. # 保存结果
  29. cv.imwrite('result.png', result)

请注意,与默认LUT中设置的暗灰色(12,12,12)不匹配,因此与类12对应的圆圈不显示出来。

请注意,上述颜色化的图像现在是3通道的RGB图像,与单通道灰度classes.png不同。

请注意,如果超过255个类别并且使用np.uint16等,你将需要重新审视代码。

以下是关于JPEG对数据的影响的小演示。首先,我们生成一个100x100的图像,并填充随机噪声,即随机颜色。然后将颜色数量减少到64,并首先保存结果为PNG,然后再次保存相同的数据为JPEG:

  1. magick -size 100x100 xc: +noise random -colors 64 -write a.png a.jpg

PNG

如何将一个1通道数组保存为具有适当颜色限制的图像

JPEG

如何将一个1通道数组保存为具有适当颜色限制的图像

它们看起来相同,对吧?现在让我们数一下每个图像中的唯一颜色数:

  1. magick a.png a.jpg -format "%f: %k\n" info:

结果

  1. a.png: 64
  2. a.jpg: 9682 # 哎呀!看看JPEG做了什么。
  3. <details>
  4. <summary>英文:</summary>
  5. First some tips...
  6. * You cannot use JPEG format for classification data. JPEG is a **lossy** format for **photographs** and is allowed to change your pixels for something that *&quot;looks pretty similar&quot;* but will be different when you re-read it. Use lossless PNG format for class data. See demonstration at end of answer.
  7. * Your classes are each represented by a single number at each pixel location - that means you need a single-channel image, a.k.a. greyscale. That means you should specify colours as a single number when drawing circles, rather than as a tuple of 2 numbers.
  8. * There&#39;s no point introducing unnecessary dependencies - you are already using **OpenCV** so read and write your images with that.
  9. These mean your code should look more like this:
  10. #!/usr/bin/env python3
  11. import numpy as np
  12. import cv2 as cv
  13. image = np.zeros(shape=[256, 256], dtype=np.uint8)
  14. cv.circle(image, center=(50, 50), radius=10, color=2, thickness= -1)
  15. cv.circle(image, center=(100, 100), radius=10, color=3, thickness= -1)
  16. cv.circle(image, center=(150, 150), radius=10, color=4, thickness= -1)
  17. cv.circle(image, center=(75, 200), radius=10, color=5, thickness= -1)
  18. cv.circle(image, center=(200, 89), radius=10, color=6, thickness= -1)
  19. # additional circles (they are part of Image #2)
  20. cv.circle(image, center=(21, 230), radius=5, color=7, thickness= -1)
  21. cv.circle(image, center=(149, 250), radius=5, color=12, thickness= -1)
  22. cv.imwrite(&#39;classes.png&#39;, image)
  23. This will save your images with very low contrast - all pixels will be in the range 0..12 out of a possible range of 0..255, so they will be dark, but still contain the information you need.
  24. [![enter image description here][1]][1]
  25. ---
  26. In order to visualise, you will need to contrast-stretch, or normalise your data to the full range. You can do that with **ImageMagick** very simply:
  27. magick classes.png -normalize classes-norm.png
  28. [![enter image description here][2]][2]
  29. If you don&#39;t want to use **ImageMagick** to normalize, the equivalent operation in **OpenCV** is:
  30. normalizedImg = cv.normalize(img, None, 0, 255, cv.NORM_MINMAX)
  31. ---
  32. If you want to apply your own colormap to the data, you can do that using a LUT *&quot;Lookup Table&quot;* like this:
  33. #!/usr/bin/env python3
  34. import sys
  35. import cv2 as cv
  36. import numpy as np
  37. if __name__ == &quot;__main__&quot;:
  38. # Check filename supplied
  39. if len(sys.argv) != 2:
  40. sys.exit(&#39;Usage: argv[0] filename&#39;)
  41. # Read greyscale class image
  42. image = cv.imread(sys.argv[1], cv.IMREAD_GRAYSCALE)
  43. # Create a 256 entry palette each with 3 (RGB) entries, initially looking like greyscale
  44. pal = np.fromfunction(lambda i,j: i, (256,3), dtype=np.uint8)
  45. #&#160;Overwrite any desired colours from grey to new colour
  46. pal[0] = [0,0,0] # 0 maps to black
  47. pal[1] = [255,0,0] # 1 maps to red
  48. pal[2] = [0,255,0] # 2 maps to green
  49. pal[3] = [0,0,255] # 3 maps to blue
  50. pal[4] = [255,255,0] # 4 maps to yellow
  51. pal[5] = [0,255,255] # 5 maps to cyan
  52. pal[6] = [255,0,255] # 6 maps to magenta
  53. pal[7] = [128,0,0] # 7 maps to dark red
  54. pal[8] = [0,128,0] # 8 maps to dark green
  55. pal[9] = [0,0,128] # 9 maps to dark blue
  56. #&#160;Convert LUT to BGR order for OpenCV
  57. pal = pal[:,::-1]
  58. # Look up each pixel in the LUT
  59. result = pal[image]
  60. # Save result
  61. cv.imwrite(&#39;result.png&#39;, result)
  62. [![enter image description here][3]][3]
  63. Obviously you could equally store the LUT in a JSON or a CSV and load it from there rather than putting it in the Python code.
  64. ---
  65. Note that the circle corresponding to class 12 doesn&#39;t show up because I was too lazy to remap that away from the dark grey I set in the default LUT (12,12,12).
  66. Note that the image as colourised above is now a 3-channel RGB image, in contrast to the single channel, greyscale `classes.png`.
  67. ---
  68. Note that you will need to revisit the code if you exceed 255 classes and use `np.uint16` and so on.
  69. ---
  70. Here&#39;s a little demonstration of what JPEG does to your data. First we generate a 100x100 image and fill it with random noise, i.e. random colours. Then reduce the number of colours to 64 and save the result first as a PNG, and then save the same data again as JPEG:
  71. magick -size 100x100 xc: +noise random -colors 64 -write a.png a.jpg
  72. **PNG**
  73. [![enter image description here][4]][4]
  74. **JPEG**
  75. [![enter image description here][5]][5]
  76. They look the same, don&#39;t they. Now let&#39;s count the number of unique colours in each:
  77. magick a.png a.jpg -format &quot;%f: %k\n&quot; info:
  78. **Result**
  79. a.png: 64
  80. a.jpg: 9682 # OOPS! Look what JPEG did.
  81. Why does JPEG do that? Because the file is 60% smaller but the images look the same:
  82. -rw-r--r--@ 1 mark staff 46256 9 Jun 11:04 a.png
  83. -rw-r--r--@ 1 mark staff 18991 9 Jun 11:04 a.jpg
  84. [1]: https://i.stack.imgur.com/qZDoq.png
  85. [2]: https://i.stack.imgur.com/lyNxm.png
  86. [3]: https://i.stack.imgur.com/6SwBT.png
  87. [4]: https://i.stack.imgur.com/WeB77.png
  88. [5]: https://i.stack.imgur.com/Hxz79.jpg
  89. </details>

huangapple
  • 本文由 发表于 2023年6月9日 08:36:34
  • 转载请务必保留本文链接:https://go.coder-hub.com/76436502.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定