基于Floyd-Steinberg的Python OpenCV图像抖动白色区域异常问题咨询
Fixing Floyd-Steinberg Dithering Edge Artifacts in OpenCV/Python
Let's break down why you're seeing those unexpected dark spots in white areas (especially the top-right corner) and fix the code step by step:
Key Issues in Your Implementation
- Reversed Pixel Coordinates: OpenCV/numpy stores images as
[row, column](i.e.,[y, x]), but your code usesim[x,y]which swaps the axes. This sends error diffusion to the wrong pixels, causing edge artifacts. - Incorrect Width/Height Assignment:
img.shapereturns(height, width, channels), sowidth,height,z=img.shapeflips your dimensions entirely—this is a major cause of edge misbehavior. - Unclamped Pixel Values: When adding quantization error, values can go below 0 or above 255. Since OpenCV uses uint8 arrays, these values wrap around (e.g., 256 becomes 0), creating unexpected dark/light pixels.
- Out-of-Bounds Access: Your loop allows
x+1ory+1to go beyond the image dimensions, leading to invalid pixel modifications.
Corrected Code
import cv2 import numpy as np def set_pixel(im, y, x, new_val): # Ensure we stay within 0-255 to prevent overflow clamped_val = np.clip(new_val, 0, 255) im[y, x] = clamped_val def quantize(im): height, width = im.shape # Floyd-Steinberg processes left-to-right, top-to-bottom for y in range(height): for x in range(width): old_pixel = im[y, x] # Quantize to 0 or 255 new_pixel = 0 if old_pixel < 127 else 255 set_pixel(im, y, x, new_pixel) quant_err = old_pixel - new_pixel # Apply error diffusion only if we're not at the edge if x + 1 < width: set_pixel(im, y, x+1, im[y, x+1] + quant_err * 7/16) if x - 1 >= 0 and y + 1 < height: set_pixel(im, y+1, x-1, im[y+1, x-1] + quant_err * 3/16) if y + 1 < height: set_pixel(im, y+1, x, im[y+1, x] + quant_err * 5/16) if x + 1 < width and y + 1 < height: set_pixel(im, y+1, x+1, im[y+1, x+1] + quant_err * 1/16) return im # Load image and process channels img = cv2.imread("/home/user/Downloads/blender_images/truck.jpg") gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) img2 = img.copy() # Correct shape assignment: height first, then width height, width, z = img.shape # Process each color channel separately blue = img[:, :, 0].copy() # Make a copy to avoid modifying original image blue = quantize(blue) green = img[:, :, 1].copy() green = quantize(green) red = img[:, :, 2].copy() red = quantize(red) gray1 = quantize(gray.copy()) # Merge channels back image = cv2.merge((blue, green, red)) # Display results cv2.imshow('original', img2) cv2.imshow('merged', image) cv2.imshow('gray', gray1) cv2.waitKey(0) cv2.destroyAllWindows() # Clean up windows after exit
What Changed?
- Fixed Coordinate System: Now
set_pixelusesim[y, x]to match numpy/OpenCV's row-column order. - Clamped Pixel Values:
np.clipensures all pixel values stay within the valid 0-255 range, eliminating overflow artifacts. - Edge Safety Checks: We only apply error diffusion if the target pixel is within the image bounds (no more out-of-bounds access).
- Copied Channels: We make copies of each color channel before processing to avoid accidentally modifying the original image data.
- Cleaned Up Window Handling: Added
cv2.destroyAllWindows()to properly close windows when you exit.
This should eliminate those weird dark spots in white areas and give you consistent Floyd-Steinberg dithering across all images.
内容的提问来源于stack exchange,提问作者Bharath Kotari
相关产品推荐
相关产品推荐

