示範如何使用手指在畫面上點選、拖曳來取得影像中 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];
}
請問在.h檔的裡面,target要怎麼宣告呢?
回覆刪除您好:
刪除target 是一個 UIImageView 喔,你可以在介面檔直接拉一個 UIImageView 出來,在與 h檔 做連接,
另外你也可以使用程式碼的方式來宣告 UIImageView @property (weak, nonatomic) IBOutlet UIImageView *target; (啟用ARC機制)
然後在 .m 檔中的 @synthesize 區段補上 target。
感謝~
刪除那backgroundImage在.h檔裡面的宣告應該是
UIImage backgroundImage;
這樣宣告的吧?
您好:
刪除這要看您是怎樣設計的唷,這邊有個小技巧,我的 backgroundImage 的大小就和畫面一樣大 320寬 480高,也就因為這樣我只要將畫面上的座標點直接套用到影像上就可以了。
再來回答你的問題,你可以把它宣告成 UIImage 並且在自己更換背景圖,或是直接宣告它是 UIImageView 在把它加入到主畫面中,因為 backgroundImage 和我們要抓的座標完全是兩件不同的事情,它只是顯示在背後給使用者看而已,希望有幫助到你。
恩恩,了解了,非常感謝你!!!
回覆刪除不用客氣
刪除請問如果要分析下一張圖時,要怎麼換?
回覆刪除例如:
宣告完 backgroundImage = [UIImage imageNamed:@"demo.png"]; 之後 //第一張圖
再次宣告 backgroundImage = [UIImage imageNamed:@"demo123.png"]; //第二張圖
偵測值將會顯示2張圖的影像合在一起之後的值
請問有什麼方法可以排除第一張圖的影響?
HI Bear
刪除我這邊沒有出現過類似的問題耶,你可以檢查看看是不是把 backgroundImage 與 畫面中所縣市的背景兩個搞混了,雖然兩個是一樣的圖片,但是卻是兩個不同的東西喔,backgroundImage 和我們要抓的座標完全是兩件不同的事情,它只是顯示在背後給使用者看而已。
沒有喔
刪除我試過了很多次了
合理的推斷應該是2張合在一起之後顯示的值
您好 BEAR:
刪除那就真的很奇怪了,我也是按照您所說的做兩次宣告,但是並沒有發現重疊的的現象。
要不要試試看以下的方法呢?雖然我覺得幫助不大就是了....
NSString *path = [[NSBundle mainBundle] pathForResource:@"demo" ofType:@"png"];
backgroundImage = [UIImage imageWithContentsOfFile:path];
ps:我是拿上面的圖片與一張全藍的圖片做測試,兩張影像大小與位元深度(32)皆相同。
謝謝,我試試看
刪除圖片不是320*480的大小
回覆刪除是否會出現異常?
您好:
刪除要看你如何設計才能決定,取得像素的數值宇圖片大小無關,你要注意的是,取的圖片的座標點,再轉換之後是否仍在陣列的範圍之中,
如果按照上述的程式碼,320*480 是畫面擷取的最大範圍,超過這個範圍的圖片,都無法取得像素值。
請問在這裡減20的用意何在??
回覆刪除int x = target.center.x - 20;
int y = target.center.y - 20;
您好:
刪除做校正用,把在螢幕上取得的座標點轉換成圖片上的像素位置。
target Image 的大小為 40 *40,他的半徑也就是 20,校正的參數也由此而來。
請問我可以把一個project寄給你們看看嗎?
回覆刪除因為換圖片在偵測一次的話,
真的會發生異常
bear 您好:
刪除好哇,
麻煩你將 code 整理好之後寄到我的信箱 milk.lord@gmail.com,
謝謝。
已寄出,十分感謝牛奶願撥出時間幫我看看問題
刪除謝謝!!!
您好:
刪除已經找到問題的所在了,如果需要處理包含透明度的圖片時,可以在 CGContextDrawImage 之前先將它做清除的動作,補上
//清空CGContextRef
CGContextClearRect(context, CGRectMake(0.0, 0.0, width, height));
才不會影響下一張圖片的取值。
文章中我也同步做了修正。
如果圖片太大張
回覆刪除無法用一個畫面就能窺得全貌
是否能用捲軸的技巧
加上適當的程式碼修改
來達到目的
您好:
刪除可以這做沒有問題,你只需要注意圖片在呈現的部分,這邊就可以使用捲軸的技巧來實作,
至於取得作標的位置可能就不適合使用 TARGET 來幫助你做判斷,你可以參考「取得點擊特定 View 時的相對座標」一文,來幫助你取得圖片的座標位置,網址如下:
http://furnacedigital.blogspot.tw/2011/07/view.html
希望對你有幫助。
請問一下程式xcode版本出錯嗎??
回覆刪除我在6.0上打了它顏色會亂跳
您好:
刪除請問一下亂跳是什麼意思?