2011-11-08 77 views
37

Câu hỏi của tôi là nếu tôi có hình ảnh Sư tử, tôi chỉ muốn thay đổi màu của con sư tử chứ không phải màu nền. Đối với điều đó tôi gọi đây là SO question nhưng nó biến màu của toàn bộ hình ảnh. Hơn nữa, hình ảnh không đẹp lắm. Tôi cần sự thay đổi màu sắc như photoshop. cho dù nó có thể làm điều này trong coregraphics hoặc tôi phải sử dụng bất kỳ thư viện khác.Làm thế nào để thay đổi một màu cụ thể trong một hình ảnh?

EDIT: Tôi cần sự thay đổi màu sắc được như iQuikColor ứng dụng

enter image description here

+2

câu hỏi hay, nếu bạn có câu trả lời hoàn hảo, tôi sẽ sử dụng số –

+0

Tôi cũng gặp vấn đề tương tự, tôi đã đăng câu hỏi [vấn đề colorfill] (http://stackoverflow.com/questions/8124308/filling -a-phần-of-an-hình ảnh-với-màu), – Marios

+0

hi bạn đã có bất kỳ ý tưởng trong này – Marios

Trả lời

20

Xem câu trả lời bên dưới để thay thế. Mỏ không cung cấp một giải pháp hoàn chỉnh.


Dưới đây là phác thảo của một giải pháp có thể sử dụng OpenCV:

  • Chuyển đổi hình ảnh từ RGB để HSV sử dụng cvCvtColor (chúng tôi chỉ muốn thay đổi màu sắc).
  • Cô lập màu với cvThreshold chỉ định một dung sai nhất định (bạn muốn có nhiều màu, không phải một màu phẳng).
  • Hủy các vùng màu dưới mức tối thiểu bằng cách sử dụng thư viện phát hiện blob như cvBlobsLib. Điều này sẽ loại bỏ các chấm có màu tương tự trong cảnh.
  • Mặt nạ màu với cvInRangeS và sử dụng mặt nạ tạo ra để áp dụng màu sắc mới.
  • cvMerge hình ảnh mới với màu sắc mới với hình ảnh được tạo bởi các kênh bão hòa và độ sáng mà bạn đã lưu ở bước một.

Có một số cổng iOS OpenCV trong mạng, ví dụ: http://www.eosgarden.com/en/opensource/opencv-ios/overview/ Tôi chưa tự thử điều này, nhưng có vẻ là hướng nghiên cứu tốt.

+0

cảm ơn jano .. Tôi sẽ thử điều này .. – Aravindhan

+3

Hi jano, tôi bị kẹt ở bước: 4, tôi đã áp dụng chức năng cvInRangeS và tôi có hình ảnh threashold nhưng không chắc chắn những gì bạn có nghĩa là "sử dụng mặt nạ kết quả để áp dụng màu sắc mới." và làm thế nào để sử dụng trong bước: 5? bạn có thể vui lòng giải thích thêm về nó – Iducool

0

Tôi không biết của một hoạt động CoreGraphics cho điều này, và tôi không thấy một bộ lọc CoreImage thích hợp cho việc này. Nếu đó là đúng, thì đây là một sự thúc đẩy đi đúng hướng:

Giả sử bạn có một CGImage (hoặc một uiImage.CGImage):

  • Bắt đầu bằng cách tạo ra một mới CGBitmapContext
  • Vẽ hình ảnh nguồn để bitmap bối cảnh
  • Lấy một handle của dữ liệu pixel của bitmap

Tìm hiểu cách bộ đệm được cấu trúc, do đó bạn có thể đúng populat mảng ea 2D của giá trị pixel mà có dạng:

typedef struct t_pixel { 
    uint8_t r, g, b, a; 
} t_pixel; 

Sau đó, tạo ra các màu sắc để xác định vị trí:

const t_pixel ColorToLocate = { 0,0,0,255 }; // << black, opaque 

Và giá trị thay thế của nó:

const t_pixel SubstitutionColor = { 255,255,255,255 }; // << white, opaque 
  • lặp qua các bitmap bộ đệm pixel của ngữ cảnh, tạo t_pixel s.
  • Khi bạn tìm thấy pixel phù hợp với ColorToLocate, hãy thay thế giá trị nguồn bằng các giá trị trong SubstitutionColor.

  • Tạo mới CGImage từ CGBitmapContext.

Đó là phần dễ dàng! Tất cả những gì cần thực hiện là mất CGImage, thay thế chính xác các màu phù hợp và tạo ra một CGImage mới.

Điều bạn muốn tinh vi hơn. Đối với nhiệm vụ này, bạn sẽ muốn có một thuật toán phát hiện cạnh tốt.

Tôi chưa sử dụng ứng dụng này mà bạn đã liên kết. Nếu nó bị giới hạn ở một vài màu, thì chúng có thể đơn giản là trao đổi các giá trị kênh, kết hợp với phát hiện cạnh (lưu ý rằng các bộ đệm cũng có thể được thể hiện trong nhiều mô hình màu - không chỉ RGBA).

Nếu (trong ứng dụng bạn đã liên kết) người dùng có thể chọn một màu, giá trị tùy ý và ngưỡng cạnh, thì bạn sẽ phải sử dụng tính năng trộn thực và phát hiện cạnh. Nếu bạn cần xem cách thực hiện điều này, bạn có thể muốn xem một gói như Gimp (đó là trình chỉnh sửa hình ảnh mở) - chúng có các thuật toán để phát hiện các cạnh và chọn theo màu.

+0

Tôi cũng đã cố gắng như thế này chỉ .. nhưng phải mất thời gian .. Có thể tôi phải sử dụng một thuật toán hiệu quả. – Aravindhan

+0

@Trisha Tôi tưởng tượng một triển khai tốt sẽ mất thời gian cho một hình ảnh có kích thước đầy đủ. Nhưng có, việc phát hiện cạnh có thể mất một thời gian để tính toán. Trên phần cứng hiện đại, bạn có thể phân chia hợp lý việc xử lý giữa một hoặc hai luồng phụ. Như bạn nói, bản ngã cũng khá quan trọng. – justin

1

Tôi có thể đề xuất bạn xem xét sử dụng OpenCV? Đó là một thư viện thao tác hình ảnh nguồn mở, và nó cũng có một cổng iOS. Có rất nhiều bài đăng trên blog về cách sử dụng và thiết lập nó.

Nó có cả đống chức năng giúp bạn thực hiện tốt công việc bạn đang cố gắng. Bạn có thể làm điều đó chỉ bằng cách sử dụng CoreGraphics, nhưng kết quả cuối cùng sẽ không trông gần như tốt như OpenCV.

Nó được phát triển bởi một số folks tại MIT, vì vậy bạn có thể mong đợi nó làm một công việc khá tốt ở những thứ như phát hiện cạnh và theo dõi đối tượng. Tôi nhớ đọc một blog về cách tách một màu nhất định khỏi một bức ảnh với OpenCV - các ví dụ cho thấy một kết quả khá tốt. Xem here để biết ví dụ. Từ đó tôi không thể tưởng tượng nó sẽ là một công việc lớn để thực sự thay đổi màu sắc riêng biệt để cái gì khác.

+0

cảm ơn jordan .. Tôi sẽ thử điều này .. – Aravindhan

13

tôi sẽ làm cho các giả định rằng bạn biết làm thế nào để thực hiện các thao tác cơ bản, do đó, những sẽ không được đưa vào giải pháp của tôi:

  • tải một hình ảnh
  • nhận được giá trị RGB của một pixel nhất định của hình ảnh được tải
  • đặt giá trị RGB của một pixel nhất định
  • hiển thị hình ảnh đã tải và/hoặc lưu nó vào đĩa.

Trước hết, hãy xem xét cách bạn có thể mô tả màu nguồn và đích. Rõ ràng bạn không thể chỉ định những giá trị này dưới dạng giá trị RGB chính xác, vì ảnh sẽ có các biến thể nhỏ về màu sắc. Ví dụ: các pixel màu xanh lục trong hình ảnh xe tải bạn đã đăng không hoàn toàn giống với màu xanh lá cây. Các mô hình màu RGB không phải là rất tốt tại thể hiện các đặc điểm màu cơ bản, vì vậy bạn sẽ nhận được kết quả tốt hơn nhiều nếu bạn chuyển đổi các điểm ảnh để HSL. Here là các hàm C để chuyển đổi RGB thành HSL và ngược lại.

Mô hình HSL màu mô tả ba khía cạnh của một màu:

  1. Huế - màu nhận thức chính - tức là màu đỏ, xanh lá cây, cam, vv
  2. Saturation - làm thế nào "đầy đủ" các màu sắc là - tức là từ đầy đủ màu sắc để không có màu sắc ở tất cả
  3. Lightness - làm thế nào sáng màu là

vì vậy, ví dụ, nếu bạn muốn tìm tất cả các điểm ảnh màu xanh lá cây trong một bức ảnh, bạn sẽ chuyển đổi từng điểm ảnh từ RGB sang HSL , sau đó tìm các giá trị H tương ứng với màu xanh lá cây, với một số dung sai cho màu "gần màu xanh". Dưới đây là một biểu đồ Huế, từ Wikipedia:

Vì vậy, trong trường hợp của bạn, bạn sẽ được nhìn pixel có một Huế 120 độ +/- số lượng. Phạm vi càng lớn thì càng có nhiều màu sắc sẽ được chọn. Nếu bạn làm cho phạm vi của bạn quá rộng, bạn sẽ bắt đầu thấy các điểm màu vàng và xanh lam được chọn, vì vậy bạn sẽ phải tìm đúng phạm vi và thậm chí bạn có thể muốn cung cấp cho người dùng các điều khiển ứng dụng của mình để chọn phạm vi này.

Ngoài việc chọn bởi Huế, bạn có thể muốn cho phép phạm vi cho Độ bão hòa và Độ sáng, do đó bạn có thể tùy chọn đặt thêm giới hạn cho pixel mà bạn muốn chọn để tô màu.

Cuối cùng, bạn có thể muốn cung cấp cho người dùng khả năng vẽ "lựa chọn lasso" để các phần cụ thể của hình ảnh có thể bị bỏ sót khỏi quá trình tô màu. Đây là cách bạn có thể nói cho các ứng dụng mà bạn muốn cơ thể của chiếc xe tải màu xanh lá cây, nhưng không phải là bánh xe màu xanh lá cây.

Một khi bạn biết bạn muốn sửa đổi điểm ảnh nào thì đã đến lúc thay đổi màu của chúng.

Cách dễ nhất để tô màu các pixel là chỉ thay đổi Hue, để nguyên Saturation và Lightness từ pixel ban đầu. Vì vậy, ví dụ, nếu bạn muốn tạo các pixel màu xanh lá cây đỏ tươi, bạn sẽ thêm 180 độ vào tất cả các giá trị Hue của các pixel đã chọn (đảm bảo bạn sử dụng phép toán modulo 360).

Nếu bạn muốn tinh vi hơn, bạn cũng có thể áp dụng các thay đổi đối với Độ bão hòa và điều đó sẽ cung cấp cho bạn phạm vi tông màu rộng hơn mà bạn có thể truy cập. Tôi nghĩ Lightness tốt hơn một mình, bạn có thể điều chỉnh nhỏ và hình ảnh sẽ vẫn đẹp, nhưng nếu bạn đi quá xa bản gốc, bạn có thể bắt đầu thấy các cạnh cứng nơi các pixel của quá trình biên giới với các pixel nền.

Khi bạn có điểm ảnh HSL được tô màu, bạn chỉ cần chuyển đổi nó trở lại RGB và ghi nó trở lại hình ảnh.

Tôi hy vọng điều này sẽ hữu ích. Một bình luận cuối cùng tôi nên làm là giá trị Hue trong mã thường được ghi lại trong khoảng 0-255, nhưng nhiều ứng dụng hiển thị chúng như một bánh xe màu với phạm vi từ 0 đến 360 độ. Ghi nhớ nó trong tâm trí!

28

Việc này mất khá nhiều thời gian, chủ yếu là vì tôi muốn thiết lập và chạy nó trong Swift bằng cách sử dụng Core Image và CIColorCube.

@ Giải thích của Miguel là tại chỗ về cách bạn cần thay thế một "phạm vi góc Huế" với "phạm vi góc Hue" khác. Bạn có thể đọc bài viết của mình ở trên để biết chi tiết về phạm vi của Hue Angle.

Tôi đã tạo một ứng dụng nhanh chóng thay thế một chiếc xe tải màu xanh mặc định bên dưới, với bất cứ thứ gì bạn chọn trên thanh trượt Hue.

enter image description here

Bạn có thể trượt thanh trượt để nói với ứng dụng những gì Huế màu sắc mà bạn muốn thay thế màu xanh với.

Tôi đang mã hóa cứng phạm vi Huế thành 60 độ, thường có vẻ bao gồm hầu hết một màu cụ thể nhưng bạn có thể chỉnh sửa nếu cần.

enter image description here

enter image description here

Chú ý rằng nó không màu lốp xe hoặc đèn đuôi vì đó là ngoài phạm vi 60 độ của màu xanh mặc định của xe tải, nhưng nó không xử lý che một cách thích hợp.

Trước tiên, bạn cần có mã để chuyển đổi RGB để HSV (giá trị Huế):

func RGBtoHSV(r : Float, g : Float, b : Float) -> (h : Float, s : Float, v : Float) { 
    var h : CGFloat = 0 
    var s : CGFloat = 0 
    var v : CGFloat = 0 
    let col = UIColor(red: CGFloat(r), green: CGFloat(g), blue: CGFloat(b), alpha: 1.0) 
    col.getHue(&h, saturation: &s, brightness: &v, alpha: nil) 
    return (Float(h), Float(s), Float(v)) 
} 

Sau đó, bạn cần phải chuyển đổi HSV để RGB. Bạn muốn sử dụng điều này khi bạn phát hiện ra một màu sắc trong phạm vi màu mong muốn của bạn (aka, một màu sắc đó là cùng một màu xanh của xe tải mặc định) để tiết kiệm bất kỳ điều chỉnh bạn thực hiện.

func HSVtoRGB(h : Float, s : Float, v : Float) -> (r : Float, g : Float, b : Float) { 
    var r : Float = 0 
    var g : Float = 0 
    var b : Float = 0 
    let C = s * v 
    let HS = h * 6.0 
    let X = C * (1.0 - fabsf(fmodf(HS, 2.0) - 1.0)) 
    if (HS >= 0 && HS < 1) { 
     r = C 
     g = X 
     b = 0 
    } else if (HS >= 1 && HS < 2) { 
     r = X 
     g = C 
     b = 0 
    } else if (HS >= 2 && HS < 3) { 
     r = 0 
     g = C 
     b = X 
    } else if (HS >= 3 && HS < 4) { 
     r = 0 
     g = X 
     b = C 
    } else if (HS >= 4 && HS < 5) { 
     r = X 
     g = 0 
     b = C 
    } else if (HS >= 5 && HS < 6) { 
     r = C 
     g = 0 
     b = X 
    } 
    let m = v - C 
    r += m 
    g += m 
    b += m 
    return (r, g, b) 
} 

Bây giờ bạn chỉ cần lặp qua một khối màu RGBA đầy đủ và "điều chỉnh" bất kỳ màu nào trong dải màu "mặc định xanh" với màu từ màu mới bạn muốn. Sau đó, sử dụng Core Image và bộ lọc CIColorCube để áp dụng khối màu đã điều chỉnh của bạn cho hình ảnh.

func render() { 
    let centerHueAngle: Float = 214.0/360.0 //default color of truck body blue 
    let destCenterHueAngle: Float = slider.value 
    let minHueAngle: Float = (214.0 - 60.0/2.0)/360 //60 degree range = +30 -30 
    let maxHueAngle: Float = (214.0 + 60.0/2.0)/360 
    var hueAdjustment = centerHueAngle - destCenterHueAngle 
    let size = 64 
    var cubeData = [Float](count: size * size * size * 4, repeatedValue: 0) 
    var rgb: [Float] = [0, 0, 0] 
    var hsv: (h : Float, s : Float, v : Float) 
    var newRGB: (r : Float, g : Float, b : Float) 
    var offset = 0 
    for var z = 0; z < size; z++ { 
     rgb[2] = Float(z)/Float(size) // blue value 
     for var y = 0; y < size; y++ { 
      rgb[1] = Float(y)/Float(size) // green value 
      for var x = 0; x < size; x++ { 
       rgb[0] = Float(x)/Float(size) // red value 
       hsv = RGBtoHSV(rgb[0], g: rgb[1], b: rgb[2]) 
       if hsv.h < minHueAngle || hsv.h > maxHueAngle { 
        newRGB.r = rgb[0] 
        newRGB.g = rgb[1] 
        newRGB.b = rgb[2] 
       } else { 
        hsv.h = destCenterHueAngle == 1 ? 0 : hsv.h - hueAdjustment //force red if slider angle is 360 
        newRGB = HSVtoRGB(hsv.h, s:hsv.s, v:hsv.v) 
       } 
       cubeData[offset] = newRGB.r 
       cubeData[offset+1] = newRGB.g 
       cubeData[offset+2] = newRGB.b 
       cubeData[offset+3] = 1.0 
       offset += 4 
      } 
     } 
    } 
    let data = NSData(bytes: cubeData, length: cubeData.count * sizeof(Float)) 
    let colorCube = CIFilter(name: "CIColorCube")! 
    colorCube.setValue(size, forKey: "inputCubeDimension") 
    colorCube.setValue(data, forKey: "inputCubeData") 
    colorCube.setValue(ciImage, forKey: kCIInputImageKey) 
    if let outImage = colorCube.outputImage { 
     let context = CIContext(options: nil) 
     let outputImageRef = context.createCGImage(outImage, fromRect: outImage.extent) 
     imageView.image = UIImage(CGImage: outputImageRef) 
    } 
} 

Bạn có thể tải xuống sample project here. Nó đang sử dụng Xcode 7 và Swift 2.0.

+0

** Bài đăng nổi bật **! Bây giờ tôi chỉ cần tìm thời gian để tải xuống dự án và tìm ra cách chuyển đổi phạm vi màu HSV của bạn thành một khối màu. Tại sao không làm cho đầu vào màu sắc một thanh trượt là tốt, và cũng là phạm vi góc màu sắc? Điều đó sẽ làm cho một mẫu linh hoạt hơn, và không phải là khó để làm. –

+0

Tôi đã viết một dự án trong khi trở lại (pre-swift) thẩm vấn iOS để xem danh sách các bộ lọc CI có sẵn và xây dựng một giao diện người dùng để thu thập các đầu vào để thử hầu hết chúng. Tôi không dành thời gian để tìm ra cách xây dựng một khối màu, vì vậy nó là một trong những ứng dụng của tôi không hỗ trợ.Bài đăng của bạn có thể mang lại cho tôi cú huých tôi cần thêm hỗ trợ cho bộ lọc khối màu. Kết quả chắc chắn trông rất tốt. –

+1

Ý của bạn là "màu trắng hoặc xám"? Bạn có nghĩa là tạo ra một chiếc xe đơn sắc? Bạn có thể cần điều chỉnh giá trị bão hòa trong dải màu đích hơn là giá trị màu sắc. Tôi đoán bạn vẫn sẽ sử dụng một phạm vi góc màu cho các màu đầu vào, và sau đó gõ xuống độ bão hòa trên các màu đầu ra. –

Các vấn đề liên quan