简体   繁体   English

在没有慢循环的情况下,在 Python 中使用条件进行逐像素操作

[英]Per-Pixel operation with conditonals in Python without slowy loops

I'm trying to apply a treshold to an image, but not a regular simple treshold.我正在尝试将阈值应用于图像,而不是常规的简单阈值。

I need to set to black pixels if they fit the conditonal, and if not, set them to white.如果它们符合条件,我需要将它们设置为黑色像素,如果不符合,则将它们设置为白色。

I could just loop over pixels, but on a 1080p image, it's far too long.我可以循环遍历像素,但在 1080p 图像上,它太长了。

I'm using HSV for the comparisons I need to make.我正在使用 HSV 进行我需要进行的比较。

Here is the conditional (this example is how I would use it if it was in a loop):这是条件(这个例子是我在循环中使用它的方式):

if abs(input_pixel_color.hue - reference.hue) < 2 and input_pixel_color.saturation >= 0.25 and input_pixel_color.brightness >= 0.42:
    set_to_black
else:
    set_to_white

input_pixel is the HSV value of the pixel in the loop. input_pixel是循环中像素的 HSV 值。

reference is a variable to be compared to. reference是要与之比较的变量。

I thought about using numpy, but I really don't know how to write this :/我想过使用 numpy,但我真的不知道如何写这个:/

Thanks in advance提前致谢

Updated更新

Now that your actual intended processing has become clearer, you would probably be better served by OpenCV inRange() function.现在您的实际预期处理已经变得更加清晰, OpenCV inRange()函数可能会更好地为您服务。 Like this:像这样:

#!/usr/local/bin/python3 

import cv2 as cv 
import numpy as np 

# Load the image and convert to HLS 
image = cv.imread("image.jpg") 
hls   = cv.cvtColor(image,cv.COLOR_BGR2HLS) 

# Define lower and uppper limits for each component 
lo = np.array([50,0,0]) 
hi = np.array([70,255,255]) 

# Mask image to only select filtered pixels 
mask = cv.inRange(hls,lo,hi) 

# Change image to white where we found our colour 
image[mask>0]=(255,255,255) 

cv.imwrite("result.png",image) 

So, if we use this image:所以,如果我们使用这个图像:

在此处输入图片说明

We are selecting Hues in the range 50-70, and making them white:我们选择 50-70 范围内的色调,并将它们设为白色:

在此处输入图片说明

If you go here to a colour converter, you can see that "Green" is Hue=120, but OpenCV divides Hues by 2 so that 360 degrees becomes 180 and still fits in a uint8.如果你去这里的颜色转换器,你可以看到“绿色”是顺= 120,但OpenCV的分歧Hues公司由2使360度成为在UINT8 180只仍然适用。 So, our 60 in the code means 120 in online colour converters.因此,代码中的 60 表示在线颜色转换器中的 120。

在此处输入图片说明

The ranges OpenCV uses for uint8 images are: OpenCV 用于 uint8 图像的范围是:

  • Hue 0..180色调 0..180
  • Lightness 0..255亮度 0..255
  • Saturation 0..255饱和度 0..255

As I said before, you should get in the habit of looking at your data types, shapes and ranges in your debugger.正如我之前所说,您应该养成在调试器中查看数据类型、形状和范围的习惯。 To see the shape , dtype , and maximum Hue, Lightness and Saturation, use:要查看shapedtype和最大色调、亮度和饱和度,请使用:

print(hls.dtype, hls.shape) 
print(hls[...,0].max())
print(hls[...,1].max())
print(hls[...,2].max())

Original Answer原答案

There are several ways to do that.有几种方法可以做到这一点。 The most performant is probably with the OpenCV function cv2.inRange() and there are plenty of answers on StackOverflow about that.性能最好的可能是OpenCV函数cv2.inRange()并且 StackOverflow 上有很多关于这个的答案。

Here is a Numpy way.这是一种 Numpy 方式。 If you read the comments and look at the printed values, you can see how to combine logical AND with logical OR and so on, as well as how to address specific channels.如果您阅读评论并查看打印的值,您可以看到如何将逻辑 AND 与逻辑 OR 等结合起来,以及如何处理特定通道。

#!/usr/bin/env python3

from random import randint, seed 
import numpy as np

# Generate a repeatable random HSV image
np.random.seed(42)
h, w = 4, 5
HSV = np.random.randint(1,100,(h,w,3),dtype=np.uint8)
print('Initial HSV\n',HSV)

# Create mask of all pixels with acceptable Hue, i.e. H > 50
HueOK = HSV[...,0] > 50
print('HueOK\n',HueOK)

# Create mask of all pixels with acceptable Saturation, i.e. S > 20 AND S < 80
SatOK = np.logical_and(HSV[...,1]>20, HSV[...,1]<80)
print('SatOK\n',SatOK)

# Create mask of all pixels with acceptable value, i.e. V < 20 OR V > 60
ValOK = np.logical_or(HSV[...,2]<20, HSV[...,2]>60)
print('ValOK\n',ValOK)

# Combine masks
combinedMask = HueOK & SatOK & ValOK
print('Combined\n',combinedMask)

# Now, if you just want to set the masked pixels to 255
HSV[combinedMask] = 255
print('Result1\n',HSV)

# Or, if you want to set the masked pixels to one value and the others to another value
HSV = np.where(combinedMask,255,0)
print('Result2\n',HSV)

Sample Output样本输出

Initial HSV
 [[[93 98 96]
  [52 62 76]
  [93  4 99]
  [15 22 47]
  [60 72 85]]

 [[26 72 61]
  [47 66 26]
  [21 45 76]
  [25 87 40]
  [25 35 83]]

 [[66 40 87]
  [24 26 75]
  [18 95 15]
  [75 86 18]
  [88 57 62]]

 [[94 86 45]
  [99 26 19]
  [37 24 63]
  [69 54  3]
  [33 33 39]]]
HueOK
 [[ True  True  True False  True]
 [False False False False False]
 [ True False False  True  True]
 [ True  True False  True False]]
SatOK
 [[False  True False  True  True]
 [ True  True  True False  True]
 [ True  True False False  True]
 [False  True  True  True  True]]
ValOK
 [[ True  True  True False  True]
 [ True False  True False  True]
 [ True  True  True  True  True]
 [False  True  True  True False]]
Combined
 [[False  True False False  True]
 [False False False False False]
 [ True False False False  True]
 [False  True False  True False]]
Result1
 [[[ 93  98  96]
  [255 255 255]
  [ 93   4  99]
  [ 15  22  47]
  [255 255 255]]

 [[ 26  72  61]
  [ 47  66  26]
  [ 21  45  76]
  [ 25  87  40]
  [ 25  35  83]]

 [[255 255 255]
  [ 24  26  75]
  [ 18  95  15]
  [ 75  86  18]
  [255 255 255]]

 [[ 94  86  45]
  [255 255 255]
  [ 37  24  63]
  [255 255 255]
  [ 33  33  39]]]
Result2
 [[  0 255   0   0 255]
 [  0   0   0   0   0]
 [255   0   0   0 255]
 [  0 255   0 255   0]]

Notes :注意事项

1) You can also access pixels not selected by the mask, using negation: 1)您还可以使用否定访问未由蒙版选择的像素:

# All unmasked pixels become 3
HSV[~combinedMask] = 3

2) The ellipsis ( ... ) is just a shortcut meaning "all other dimensions I didn't bother listing" , so HSV[...,1] is the same as HSV[:,:,1] 2) 省略号 ( ... ) 只是一个快捷方式,意思是“我没有打扰列出的所有其他维度” ,所以HSV[...,1]HSV[:,:,1]

3) If you don't like writing HSV[...,0] for Hue, and HSV[...,1] for Saturation, you can split the channels 3) 如果您不喜欢为 Hue 编写HSV[...,0] ,为 Saturation 编写HSV[...,0] HSV[...,1] ,则可以拆分通道

H, S, V = cv2.split(HSV)

Then you can just use H instead of HSV[...,0] .然后你可以使用H而不是HSV[...,0] When you are finished, if you want to re-assemble the channels back into a 3-channel image, you can do:完成后,如果要将通道重新组合成 3 通道图像,可以执行以下操作:

HSV = cv2.merge((H,S,V))

or或者

HSV = np.dstack((H,S,V))

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM