en

hi, it seems you are using microsoft internet explorer. it doesn't match web standard and causes problems browsing this site. please please please use mozilla firefox or google chrome instead. thank you!

zh

哦哦!您正在使用Internet Explorer 瀏覽器,它與我們的網頁標準並不相容,可能會導致畫面顯示不正常。
請改用 Mozilla Firefox 或者 Google Chrome 才能正常瀏覽本網站,謝謝!

12.08.2010

UIImage 內 Pixel / 像素的 RGBA 值取得方式

 

示範如何使用手指在畫面上點選、拖曳來取得影像中 Pixel 內對應的 RGBA 數值。(View-based Template)

首先在 Interface Builder 中放置好底圖(backgroundImage)與模擬手指點選區域的影像(target)。為了能在一開使執行程式時就先取得 target 下的 RGBA 值,所以在程式進入點就先呼叫一次取得 RGBA 參數值的函式,其程式碼如下。

- (void)viewDidLoad {
    [super viewDidLoad];

    //將影像讀入Cache中
    backgroundImage = [UIImage imageNamed:@"demo.png"];

    //自行定義的函式,正規化Target影像的中心點座標來符合底圖的座標
    [self normalizeTargetLocation];
}

//自行定義的函式,正規化Target影像的中心點座標來符合底圖的座標
- (void)normalizeTargetLocation {
    int x = target.center.x - 20;
    int y = target.center.y - 20;

    //自行定義的函式,取得影像座標上的RGBA值
    [self getRGBAFromImage:backgroundImage atX:x andY:y];
}

//自行定義的函式,取得影像座標上的RGBA值
- (void)getRGBAFromImage:(UIImage *)image atX:(int)xx andY:(int)yy {

    CGImageRef imageRef = [image CGImage];
    int width = CGImageGetWidth(imageRef);
    int height = CGImageGetHeight(imageRef);

    //從image的data buffer中取得影像,放入格式化後的rawData中
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    unsigned char *rawData = malloc(height * width * 4);
    NSUInteger bytesPerPixel = 4;
    NSUInteger bytesPerRow = bytesPerPixel * width;
    NSUInteger bitsPerComponent = 8;
    CGContextRef context = CGBitmapContextCreate(rawData,
                                                 width,
                                                 height,
                                                 bitsPerComponent,
                                                 bytesPerRow,
                                                 colorSpace,
                                                 kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
    CGColorSpaceRelease(colorSpace);

    //清空CGContextRef再繪製
    CGContextClearRect(context, CGRectMake(0.0, 0.0, width, height));
    CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef);
    CGContextRelease(context);

    //將XY座標轉成一維陣列
    int byteIndex = (bytesPerRow * yy) + (bytesPerPixel *xx);

    //取得RGBA位元的資料
    int red = rawData[byteIndex];
    int green = rawData[byteIndex + 1];
    int blue = rawData[byteIndex + 2];
    int alpha = rawData[byteIndex + 3];

    //利用RGB計算灰階的亮度值
    int gray = (0.299 * red) + (0.587 * green) + (0.114 * blue);

    //輸出至View上
    redLabel.text = [NSString stringWithFormat:@"%d", red];
    greenLabel.text = [NSString stringWithFormat:@"%d", green];
    blueLabel.text = [NSString stringWithFormat:@"%d", blue];
    alphaLabel.text = [NSString stringWithFormat:@"%d", alpha];
    grayLabel.text = [NSString stringWithFormat:@"%d", gray];

    free(rawData);
}

上述程式碼已經完成取得對應 Pixel 中的 RGBA 參數值,下列程式碼將實做以手指觸碰螢幕來改變 target 影像的位置,藉此取得對應的 RGBA 參數值。

//對畫面點擊所觸發的函式
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {

    //宣告一個UITouch的指標來存放事件觸發時所擷取到的狀態
    UITouch *touch = [[event allTouches] anyObject];

    //設定偏位移量
    touchOffsetX = target.center.x - [touch locationInView:touch.view].x;
    touchOffsetY = target.center.y - [touch locationInView:touch.view].y;
}

//對畫面進行拖曳時所觸發的函式
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {

    //宣告一個UITouch的指標來存放事件觸發時所擷取到的狀態
    UITouch *touch = [[event allTouches] anyObject];

    //計算偏位移量
    int distanceMovedX = ([touch locationInView:touch.view].x + touchOffsetX) - target.center.x;
    int distanceMovedY = ([touch locationInView:touch.view].y + touchOffsetY) - target.center.y;

    int newX = target.center.x + distanceMovedX;
    int newY = target.center.y + distanceMovedY;

    if (newX > 20 && newX < 300) {
        target.center = CGPointMake(newX, target.center.y);
    }

    if (newY > 20 && newY < 440) {
        target.center = CGPointMake(target.center.x, newY);
    }

    //自行定義的函式,正規化Target影像的中心點座標來符合底圖的座標
    [self normalizeTargetLocation];
}






23 則留言:

  1. 匿名6/29/2012

    請問在.h檔的裡面,target要怎麼宣告呢?

    回覆刪除
    回覆
    1. 您好:

      target 是一個 UIImageView 喔,你可以在介面檔直接拉一個 UIImageView 出來,在與 h檔 做連接,

      另外你也可以使用程式碼的方式來宣告 UIImageView @property (weak, nonatomic) IBOutlet UIImageView *target; (啟用ARC機制)

      然後在 .m 檔中的 @synthesize 區段補上 target。

      刪除
    2. 匿名6/29/2012

      感謝~
      那backgroundImage在.h檔裡面的宣告應該是
      UIImage backgroundImage;
      這樣宣告的吧?

      刪除
    3. 您好:

      這要看您是怎樣設計的唷,這邊有個小技巧,我的 backgroundImage 的大小就和畫面一樣大 320寬 480高,也就因為這樣我只要將畫面上的座標點直接套用到影像上就可以了。

      再來回答你的問題,你可以把它宣告成 UIImage 並且在自己更換背景圖,或是直接宣告它是 UIImageView 在把它加入到主畫面中,因為 backgroundImage 和我們要抓的座標完全是兩件不同的事情,它只是顯示在背後給使用者看而已,希望有幫助到你。

      刪除
  2. 匿名6/29/2012

    恩恩,了解了,非常感謝你!!!

    回覆刪除
  3. 請問如果要分析下一張圖時,要怎麼換?
    例如:
    宣告完 backgroundImage = [UIImage imageNamed:@"demo.png"]; 之後 //第一張圖
    再次宣告 backgroundImage = [UIImage imageNamed:@"demo123.png"]; //第二張圖
    偵測值將會顯示2張圖的影像合在一起之後的值
    請問有什麼方法可以排除第一張圖的影響?

    回覆刪除
    回覆
    1. HI Bear

      我這邊沒有出現過類似的問題耶,你可以檢查看看是不是把 backgroundImage 與 畫面中所縣市的背景兩個搞混了,雖然兩個是一樣的圖片,但是卻是兩個不同的東西喔,backgroundImage 和我們要抓的座標完全是兩件不同的事情,它只是顯示在背後給使用者看而已。

      刪除
    2. 沒有喔
      我試過了很多次了
      合理的推斷應該是2張合在一起之後顯示的值

      刪除
    3. 您好 BEAR:

      那就真的很奇怪了,我也是按照您所說的做兩次宣告,但是並沒有發現重疊的的現象。
      要不要試試看以下的方法呢?雖然我覺得幫助不大就是了....

      NSString *path = [[NSBundle mainBundle] pathForResource:@"demo" ofType:@"png"];
      backgroundImage = [UIImage imageWithContentsOfFile:path];

      ps:我是拿上面的圖片與一張全藍的圖片做測試,兩張影像大小與位元深度(32)皆相同。

      刪除
    4. 謝謝,我試試看

      刪除
  4. 匿名7/12/2012

    圖片不是320*480的大小
    是否會出現異常?

    回覆刪除
    回覆
    1. 您好:

      要看你如何設計才能決定,取得像素的數值宇圖片大小無關,你要注意的是,取的圖片的座標點,再轉換之後是否仍在陣列的範圍之中,

      如果按照上述的程式碼,320*480 是畫面擷取的最大範圍,超過這個範圍的圖片,都無法取得像素值。

      刪除
  5. 匿名7/13/2012

    請問在這裡減20的用意何在??
    int x = target.center.x - 20;
    int y = target.center.y - 20;

    回覆刪除
    回覆
    1. 您好:

      做校正用,把在螢幕上取得的座標點轉換成圖片上的像素位置。

      target Image 的大小為 40 *40,他的半徑也就是 20,校正的參數也由此而來。

      刪除
  6. 請問我可以把一個project寄給你們看看嗎?
    因為換圖片在偵測一次的話,
    真的會發生異常

    回覆刪除
    回覆
    1. bear 您好:

      好哇,
      麻煩你將 code 整理好之後寄到我的信箱 milk.lord@gmail.com,

      謝謝。

      刪除
    2. 已寄出,十分感謝牛奶願撥出時間幫我看看問題
      謝謝!!!

      刪除
    3. 您好:

      已經找到問題的所在了,如果需要處理包含透明度的圖片時,可以在 CGContextDrawImage 之前先將它做清除的動作,補上
      //清空CGContextRef
      CGContextClearRect(context, CGRectMake(0.0, 0.0, width, height));

      才不會影響下一張圖片的取值。

      文章中我也同步做了修正。

      刪除
  7. 匿名9/05/2012

    如果圖片太大張
    無法用一個畫面就能窺得全貌
    是否能用捲軸的技巧
    加上適當的程式碼修改
    來達到目的

    回覆刪除
    回覆
    1. 您好:
      可以這做沒有問題,你只需要注意圖片在呈現的部分,這邊就可以使用捲軸的技巧來實作,
      至於取得作標的位置可能就不適合使用 TARGET 來幫助你做判斷,你可以參考「取得點擊特定 View 時的相對座標」一文,來幫助你取得圖片的座標位置,網址如下:

      http://furnacedigital.blogspot.tw/2011/07/view.html

      希望對你有幫助。

      刪除
  8. 請問一下程式xcode版本出錯嗎??
    我在6.0上打了它顏色會亂跳

    回覆刪除
    回覆
    1. 您好:

      請問一下亂跳是什麼意思?

      刪除