簡體   English   中英

如何更改UIImage / UIImageView的單個像素的顏色

[英]How to change colour of individual pixel of UIImage/UIImageView

我有一個UIImageView,我對其應用了過濾器:

testImageView.layer.magnificationFilter = kCAFilterNearest;

使各個像素可見。 此UIImageView在UIScrollView中,並且圖像本身為1000x1000。 我使用以下代碼來檢測哪個像素已被點擊:

我首先設置了輕擊手勢識別器:

UITapGestureRecognizer *scrollTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(singleTapGestureCaptured: )];
scrollTap.numberOfTapsRequired = 1;
[mainScrollView addGestureRecognizer:scrollTap];

然后使用拍子的位置來產生拍子的坐標,UIImageView的像素通過該坐標被拍出:

- (void)singleTapGestureCaptured:(UITapGestureRecognizer *)gesture
{
    CGPoint touchPoint = [gesture locationInView:testImageView];

    NSLog(@"%f is X pixel num, %f is Y pixel num ; %f is width of imageview", (touchPoint.x/testImageView.bounds.size.width)*1000, (touchPoint.y/testImageView.bounds.size.width)*1000, testImageView.bounds.size.width);

}

我希望能夠點按一個像素並進行顏色更改。 但是,我發現的所有StackOverflow帖子都沒有有效的答案或尚未過時的答案。 但是,對於熟練的編碼人員,您也許可以幫助我破譯較舊的文章,使之有用,或者使用我上面的代碼來檢測UIImageView的哪個像素,自行制作簡單的修復程序。

感謝所有幫助。

編輯為originaluser2:

在跟隨originaluser2的帖子之后,當我在他的物理設備上通過他的示例GitHub項目運行代碼時,運行代碼可以完美運行。 但是,當我在自己的應用程序中運行相同的代碼時,遇到圖片被空格替換的錯誤,並出現以下錯誤:

<Error>: Unsupported pixel description - 3 components, 16 bits-per-component, 64 bits-per-pixel
<Error>: CGBitmapContextCreateWithData: failed to create delegate.
<Error>: CGContextDrawImage: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable.
<Error>: CGBitmapContextCreateImage: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable.

該代碼顯然可以正常工作,正如我在手機上對其進行測試所證明的那樣。 但是,相同的代碼在我自己的項目中產生了一些問題。 盡管我懷疑它們都是由一兩個簡單的中心問題引起的。 我該如何解決這些錯誤?

您將需要將此問題分解為多個步驟。

  1. 獲取圖像坐標系中觸摸點的坐標
  2. 獲取要更改的像素的x和y位置
  3. 創建一個位圖上下文,並用新顏色的組件替換給定像素的組件。

首先,要獲取圖像坐標系中觸摸點的坐標,可以使用我在UIImageView編寫的類別方法。 這將返回一個CGAffineTransform ,它將根據視圖的內容模式將一個點從視圖坐標映射到圖像坐標。

@interface UIImageView (PointConversionCatagory)

@property (nonatomic, readonly) CGAffineTransform viewToImageTransform;
@property (nonatomic, readonly) CGAffineTransform imageToViewTransform;

@end

@implementation UIImageView (PointConversionCatagory)

-(CGAffineTransform) viewToImageTransform {

    UIViewContentMode contentMode = self.contentMode;

    // failure conditions. If any of these are met – return the identity transform
    if (!self.image || self.frame.size.width == 0 || self.frame.size.height == 0 ||
        (contentMode != UIViewContentModeScaleToFill && contentMode != UIViewContentModeScaleAspectFill && contentMode != UIViewContentModeScaleAspectFit)) {
        return CGAffineTransformIdentity;
    }

    // the width and height ratios
    CGFloat rWidth = self.image.size.width/self.frame.size.width;
    CGFloat rHeight = self.image.size.height/self.frame.size.height;

    // whether the image will be scaled according to width
    BOOL imageWiderThanView = rWidth > rHeight;

    if (contentMode == UIViewContentModeScaleAspectFit || contentMode == UIViewContentModeScaleAspectFill) {

        // The ratio to scale both the x and y axis by
        CGFloat ratio = ((imageWiderThanView && contentMode == UIViewContentModeScaleAspectFit) || (!imageWiderThanView && contentMode == UIViewContentModeScaleAspectFill)) ? rWidth:rHeight;

        // The x-offset of the inner rect as it gets centered
        CGFloat xOffset = (self.image.size.width-(self.frame.size.width*ratio))*0.5;

        // The y-offset of the inner rect as it gets centered
        CGFloat yOffset = (self.image.size.height-(self.frame.size.height*ratio))*0.5;

        return CGAffineTransformConcat(CGAffineTransformMakeScale(ratio, ratio), CGAffineTransformMakeTranslation(xOffset, yOffset));
    } else {
        return CGAffineTransformMakeScale(rWidth, rHeight);
    }
}

-(CGAffineTransform) imageToViewTransform {
    return CGAffineTransformInvert(self.viewToImageTransform);
}

@end

這里沒有什么復雜的,只是比例尺適合/填充的一些額外邏輯,以確保將圖像居中考慮在內。 如果您要在屏幕上以1:1的比例顯示圖片,則可以完全跳過此步驟。

接下來,您需要獲取要更改的像素的x和y位置。 這非常簡單–您只想使用上述類別屬性viewToImageTransform來獲取圖像坐標系中的像素,然后使用floor將值進行積分。

UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(imageViewWasTapped:)];
tapGesture.numberOfTapsRequired = 1;
[imageView addGestureRecognizer:tapGesture];

...

-(void) imageViewWasTapped:(UIGestureRecognizer*)tapGesture {

    if (!imageView.image) {
        return;
    }

    // get the pixel position
    CGPoint pt = CGPointApplyAffineTransform([tapGesture locationInView:imageView], imageView.viewToImageTransform);
    PixelPosition pixelPos = {(NSInteger)floor(pt.x), (NSInteger)floor(pt.y)};

    // replace image with new image, with the pixel replaced
    imageView.image = [imageView.image imageWithPixel:pixelPos replacedByColor:[UIColor colorWithRed:0 green:1 blue:1 alpha:1.0]];
}

最后,您將要使用我的另一個分類方法– imageWithPixel:replacedByColor:用給定顏色的替換像素獲取新圖像。

/// A simple struct to represent the position of a pixel
struct PixelPosition {
    NSInteger x;
    NSInteger y;
};

typedef struct PixelPosition PixelPosition;

@interface UIImage (UIImagePixelManipulationCatagory)

@end

@implementation UIImage (UIImagePixelManipulationCatagory)

-(UIImage*) imageWithPixel:(PixelPosition)pixelPosition replacedByColor:(UIColor*)color {

    // components of replacement color – in a 255 UInt8 format (fairly standard bitmap format)
    const CGFloat* colorComponents = CGColorGetComponents(color.CGColor);
    UInt8* color255Components = calloc(sizeof(UInt8), 4);
    for (int i = 0; i < 4; i++) color255Components[i] = (UInt8)round(colorComponents[i]*255.0);

    // raw image reference
    CGImageRef rawImage = self.CGImage;

    // image attributes
    size_t width = CGImageGetWidth(rawImage);
    size_t height = CGImageGetHeight(rawImage);
    CGRect rect = {CGPointZero, {width, height}};

    // image format
    size_t bitsPerComponent = 8;
    size_t bytesPerRow = width*4;

    // the bitmap info
    CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big;

    // data pointer – stores an array of the pixel components. For example (r0, b0, g0, a0, r1, g1, b1, a1 .... rn, gn, bn, an)
    UInt8* data = calloc(bytesPerRow, height);

    // get new RGB color space
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();

    // create bitmap context
    CGContextRef ctx = CGBitmapContextCreate(data, width, height, bitsPerComponent, bytesPerRow, colorSpace, bitmapInfo);

    // draw image into context (populating the data array while doing so)
    CGContextDrawImage(ctx, rect, rawImage);

    // get the index of the pixel (4 components times the x position plus the y position times the row width)
    NSInteger pixelIndex = 4*(pixelPosition.x+(pixelPosition.y*width));

    // set the pixel components to the color components
    data[pixelIndex] = color255Components[0]; // r
    data[pixelIndex+1] = color255Components[1]; // g
    data[pixelIndex+2] = color255Components[2]; // b
    data[pixelIndex+3] = color255Components[3]; // a

    // get image from context
    CGImageRef img = CGBitmapContextCreateImage(ctx);

    // clean up
    free(color255Components);
    CGContextRelease(ctx);
    CGColorSpaceRelease(colorSpace);
    free(data);

    UIImage* returnImage = [UIImage imageWithCGImage:img];
    CGImageRelease(img);

    return returnImage;
}

@end

首先,以255 UInt8格式找出要寫入像素之一的顏色的成分。 接下來,它將使用輸入圖像的給定屬性創建一個新的位圖上下文。

此方法的重要部分是:

// get the index of the pixel (4 components times the x position plus the y position times the row width)
NSInteger pixelIndex = 4*(pixelPosition.x+(pixelPosition.y*width));

// set the pixel components to the color components
data[pixelIndex] = color255Components[0]; // r
data[pixelIndex+1] = color255Components[1]; // g
data[pixelIndex+2] = color255Components[2]; // b
data[pixelIndex+3] = color255Components[3]; // a

這樣做是根據給定像素的索引(基於像素的x和y坐標)–然后使用該索引用替換顏色的顏色分量替換該像素的分量數據。

最后,我們從位圖上下文中獲取圖像並執行一些清理。

最終結果:

在此處輸入圖片說明


完整項目: https//github.com/hamishknight/Pixel-Color-Changing

您可以嘗試以下操作:

UIImage *originalImage = [UIImage imageNamed:@"something"];

CGSize size = originalImage.size;

UIGraphicsBeginImageContext(size);

[originalImage drawInRect:CGRectMake(0, 0, size.width, size.height)];

// myColor is an instance of UIColor
[myColor setFill];
UIRectFill(CGRectMake(someX, someY, 1, 1);

UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();

UIGraphicsEndImageContext();

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM