Reputation: 371
I am working on a video capture project with OpenCV. I have been able to get the video capture to work but I want to change the camera filter to the "regular" spectrum so that the banana isn't blue and is yellow lol. This would be the RGB spectrum. I do the conversions in my program in the for loop but my camera view is still BRG. Here is an image.
How can I get the image view to show "true" colors? Here is my code.
Mat to Image Class
import java.awt.Color;
import java.awt.image.BufferedImage;
import org.opencv.core.Core;
import org.opencv.core.Mat;
public class Mat2Image {
Mat mat = new Mat();
private int argb = 0;
Color c = new Color(argb, true);
BufferedImage img;
byte [] dat;
Mat2Image(){
}
public Mat2Image(Mat mat) {
getSpace(mat);
}
public void getSpace(Mat mat) {
this.mat = mat;
int w = mat.cols(), h = mat.rows();
if (dat == null || dat.length != w * h * 3)
dat = new byte[w * h * 3];
if (img == null || img.getWidth() != w || img.getHeight() != h || img.getType() != BufferedImage.TYPE_3BYTE_BGR)
img = new BufferedImage(w, h, BufferedImage.TYPE_3BYTE_BGR);
int[] holder = new int[w * h];
holder = img.getData().getPixels(0, 0, w, h, (int[]) null);
for (int i = 0; i < dat.length; i++) {
int blueChannel = (holder[i] & 0xFF0000) >> 4;
int greenChannel = (holder[i] & 0xFF00) >> 2;
int redChannel = (holder[i] & 0xFF);
int rgb = (blueChannel) & (greenChannel << 2) & (redChannel << 4);
dat[i] = (byte) rgb;
}
}
BufferedImage getImage(Mat mat) {
getSpace(mat);
mat.get(0, 0, dat);
img.getRaster().setDataElements(0, 0, mat.cols(), mat.rows(), dat);
return img;
}
static {
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
}
}
Video Capture Class
public class VideoCap {
static {
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
}
int currFrame = 0;
VideoCapture cap;
Mat2Image mat2Img = new Mat2Image();
VideoCap() {
cap = new VideoCapture();
cap.open(0);
}
BufferedImage getOneFrame() {
currFrame++;
//currFrame is just to stop the camera of after the running for a bit.
if (currFrame == 20) {
cap.read(mat2Img.mat);
System.exit(0);
}
cap.read(mat2Img.mat);
return mat2Img.getImage(mat2Img.mat);
}
}
My JFrame Class
public class MyFrame extends JFrame {
private JPanel contentPane;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
MyFrame frame = new MyFrame();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public MyFrame() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100,100,650,490);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5,5,5,5));
setContentPane(contentPane);
contentPane.setLayout(null);
new MyThread().start();
}
VideoCap videoCap = new VideoCap();
public void paint(Graphics g){
g = contentPane.getGraphics();
g.drawImage(videoCap.getOneFrame(), 0, 0, this);
}
class MyThread extends Thread{
@Override
public void run() {
for (;;){
repaint();
try { Thread.sleep(30);
} catch (InterruptedException e) { }
}
}
}
}
Upvotes: 0
Views: 3770
Reputation: 371
I spent time yesterday trying to break it down and I finally got it to work. The first thing I did was allocate for the new image. which is done in getSpace
. Then I convert the allocated image, then capture the data from the matrix, then put the data into the image. The last 3 steps all take place in getImage
. Here is how I solved it.
public void getSpace(Mat mat) {
this.mat = mat;
int w = mat.cols(), h = mat.rows();
if (dat == null || dat.length != w * h * 3)
dat = new byte[w * h * 3];
if (img == null || img.getWidth() != w || img.getHeight() != h || img.getType() != BufferedImage.TYPE_3BYTE_BGR)
img = new BufferedImage(w, h, BufferedImage.TYPE_3BYTE_BGR);
}
BufferedImage getImage(Mat mat) {
getSpace(mat);
mat.get(0, 0, dat);
for (int i = 0; i < dat.length; i+= 3) {
byte redChannel = dat[i];
byte blueChannel = dat[i+2];
dat[i] = blueChannel;
dat[i+2] = redChannel;
}
img.getRaster().setDataElements(0, 0, mat.cols(), mat.rows(), dat);
return img;
}
The remaining code from the Mat2Image class should be the same that I didn't add in the answer and you can try it out. :)
Upvotes: 1
Reputation: 31269
You're doing the rearranging of the colors wrong - you're not shifting by enough bits, and you should be using the OR (|
) rather than the AND (&
) operator to recombine the color.
Your code:
int blueChannel = (holder[i] & 0xFF0000) >> 4;
int greenChannel = (holder[i] & 0xFF00) >> 2;
int redChannel = (holder[i] & 0xFF);
int rgb = (blueChannel) & (greenChannel << 2) & (redChannel << 4);
Should become:
int blueChannel = (holder[i] & 0xFF0000) >> 16;
int greenChannel = (holder[i] & 0xFF00) >> 8;
int redChannel = (holder[i] & 0xFF);
int rgb = (blueChannel) | (greenChannel << 8) | (redChannel << 16);
EDIT
Furthermore, it looks like you're not using an int[]
for your target image, but rather a 3-byte encoding for the color. So it's more likely that you need to do:
for (int i = 0; i < holder.length; i++) {
byte blueChannel = (byte) ((holder[i] & 0xFF0000) >> 16);
byte greenChannel = (byte) ((holder[i] & 0xFF00) >> 8);
byte redChannel = (byte) (holder[i] & 0xFF);
dat[i*3] = redChannel;
dat[i*3+1] = greenChannel;
dat[i*3+2] = blueChannel;
But it's not clear to me anymore whether you are going from BGR to RGB or the reverse from RGB to BGR. You may need to change the order of the last 3 assignments to get this right.
Upvotes: 1