diff --git a/wpilibc/wpilibC++Devices/include/nivision.h b/wpilibc/wpilibC++Devices/include/nivision.h index 14de95d41c..09bb9842e7 100644 --- a/wpilibc/wpilibC++Devices/include/nivision.h +++ b/wpilibc/wpilibC++Devices/include/nivision.h @@ -5339,5 +5339,7 @@ IMAQ_FUNC ReadTextReport* IMAQ_STDCALL imaqReadText(const Image* image, c IMAQ_FUNC ThresholdData* IMAQ_STDCALL imaqAutoThreshold(Image* dest, Image* source, int numClasses, ThresholdMethod method); IMAQ_FUNC ColorHistogramReport* IMAQ_STDCALL imaqColorHistogram(Image* image, int numClasses, ColorMode mode, const Image* mask); IMAQ_FUNC RakeReport* IMAQ_STDCALL imaqRake(const Image* image, const ROI* roi, RakeDirection direction, EdgeProcess process, const RakeOptions* options); + +IMAQ_FUNC int IMAQ_STDCALL Priv_ReadJPEGString_C(Image* image, const unsigned char* string, unsigned int stringLength); #endif diff --git a/wpilibc/wpilibC++Devices/src/Vision/AxisCamera.cpp b/wpilibc/wpilibC++Devices/src/Vision/AxisCamera.cpp index 5342111e77..47658d0eee 100644 --- a/wpilibc/wpilibC++Devices/src/Vision/AxisCamera.cpp +++ b/wpilibc/wpilibC++Devices/src/Vision/AxisCamera.cpp @@ -18,10 +18,6 @@ #include #include -/** Private NI function to decode JPEG */ -IMAQ_FUNC int Priv_ReadJPEGString_C(Image* _image, const unsigned char* _string, - uint32_t _stringLength); - static const unsigned int kMaxPacketSize = 1536; static const unsigned int kImageBufferAllocationIncrement = 1000; diff --git a/wpilibj/wpilibJavaDevices/src/main/java/com/ni/vision/NIVision.java b/wpilibj/wpilibJavaDevices/src/main/java/com/ni/vision/NIVision.java index a20aaf89c2..e9a5db39ab 100644 --- a/wpilibj/wpilibJavaDevices/src/main/java/com/ni/vision/NIVision.java +++ b/wpilibj/wpilibJavaDevices/src/main/java/com/ni/vision/NIVision.java @@ -25004,6 +25004,16 @@ public class NIVision { } private static native long _imaqRake(long image, long roi, int direction, int process, long options); + public static void Priv_ReadJPEGString_C(Image image, byte[] string) { + int stringLength = string.length; + ByteBuffer string_buf = null; + string_buf = ByteBuffer.allocateDirect(string.length); + putBytes(string_buf, string, 0, string.length); + _Priv_ReadJPEGString_C(image.getAddress(), getByteBufferAddress(string_buf), stringLength); + + } + private static native void _Priv_ReadJPEGString_C(long image, long string, int stringLength); + /** * Purpose : Include file for NI-IMAQdx library support. */ diff --git a/wpilibj/wpilibJavaDevices/src/main/java/edu/wpi/first/wpilibj/camera/AxisCamera.java b/wpilibj/wpilibJavaDevices/src/main/java/edu/wpi/first/wpilibj/camera/AxisCamera.java deleted file mode 100644 index 3325ebddb9..0000000000 --- a/wpilibj/wpilibJavaDevices/src/main/java/edu/wpi/first/wpilibj/camera/AxisCamera.java +++ /dev/null @@ -1,488 +0,0 @@ -/*----------------------------------------------------------------------------*/ -/* Copyright (c) FIRST 2008-2012. All Rights Reserved. */ -/* Open Source Software - may be modified and shared by FRC teams. The code */ -/* must be accompanied by the FIRST BSD license file in the root directory of */ -/* the project. */ -/*----------------------------------------------------------------------------*/ -package edu.wpi.first.wpilibj.camera; - -import com.sun.jna.BlockingFunction; -import com.sun.jna.Function; -import com.sun.jna.NativeLibrary; -import com.sun.jna.Pointer; -import com.sun.jna.TaskExecutor; -import com.sun.squawk.VM; -import edu.wpi.first.wpilibj.DriverStation; -import edu.wpi.first.wpilibj.communication.UsageReporting; -import edu.wpi.first.wpilibj.image.ColorImage; -import edu.wpi.first.wpilibj.image.HSLImage; -import edu.wpi.first.wpilibj.image.NIVisionException; -import edu.wpi.first.wpilibj.util.BoundaryException; - -//TODO figure out where to use finally to free resources -//TODO go through old camera code and make sure all features are implemented -//TODO make work with all three passwords -//TODO get images of different types -//TODO continue attempting to connect until succesful -//TODO optimize and use Pointers in all locations which make sense - possibly JNA memcpy? -/** - * This class is a singleton used to configure and get images from the axis camera. - * @author dtjones - */ -public class AxisCamera { - - private static AxisCamera m_instance = null; - - /** - * Enumaration representing the different values which exposure may be set to. - */ - public static class ExposureT { - - /** - * The integer value of the enumeration. - */ - public final int value; - static final ExposureT[] allValues = new ExposureT[4]; - /** - * The Axis camera automatically determines what exposure level to use. - */ - public static final ExposureT automatic = new ExposureT(0); - /** - * Hold the current exposure level. - */ - public static final ExposureT hold = new ExposureT(1); - /** - * Set exposure for flicker free 50 hz. - */ - public static final ExposureT flickerfree50 = new ExposureT(2); - /** - * Set exposure for flicker free 60 hz. - */ - public static final ExposureT flickerfree60 = new ExposureT(3); - - private ExposureT(int value) { - this.value = value; - allValues[value] = this; - } - - private static ExposureT get(int val) { - return allValues[val]; - } - } - - /** - * Enumeration representing the different values which white balence may be - * set to. - */ - public static class WhiteBalanceT { - - /** - * The integer value of the enumeration. - */ - public final int value; - static final WhiteBalanceT[] allValues = new WhiteBalanceT[7]; - /** - * The axis camera automatically adjusts the whit balance. - */ - public static final WhiteBalanceT automatic = new WhiteBalanceT(0); - /** - * Hold the current white balance. - */ - public static final WhiteBalanceT hold = new WhiteBalanceT(1); - /** - * White balance for outdoors. - */ - public static final WhiteBalanceT fixedOutdoor1 = new WhiteBalanceT(2); - /** - * White balance for outdoors. - */ - public static final WhiteBalanceT fixedOutdoor2 = new WhiteBalanceT(3); - /** - * White balance for indoors. - */ - public static final WhiteBalanceT fixedIndoor = new WhiteBalanceT(4); - /** - * White balance for fourescent lighting. - */ - public static final WhiteBalanceT fixedFlour1 = new WhiteBalanceT(5); - /** - * White balance for fourescent lighting. - */ - public static final WhiteBalanceT fixedFlour2 = new WhiteBalanceT(6); - - private WhiteBalanceT(int value) { - this.value = value; - allValues[value] = this; - } - - private static WhiteBalanceT get(int value) { - return allValues[value]; - } - } - - /** - * Enumeration representing the image resoultion provided by the camera. - */ - public static class ResolutionT { - - /** - * The integer value of the enumeration. - */ - public final int value; - /** - * Number of pixels wide. - */ - public final int width; - /** - * Number of pixels tall. - */ - public final int height; - static final ResolutionT[] allValues = new ResolutionT[4]; - /** - * ImageBase is 640 pixels wide by 480 tall - */ - public static final ResolutionT k640x480 = new ResolutionT(0, 640, 480); - /** - * ImageBase is 640 pixels wide by 360 tall - */ - public static final ResolutionT k640x360 = new ResolutionT(1, 640, 360); - /** - * ImageBase is 320 pixels wide by 240 tall - */ - public static final ResolutionT k320x240 = new ResolutionT(2, 320, 240); - /** - * ImageBase is 160 pixels wide by 120 tall - */ - public static final ResolutionT k160x120 = new ResolutionT(3, 160, 120); - - private ResolutionT(int value, int horizontal, int vertical) { - this.value = value; - this.width = horizontal; - this.height = vertical; - allValues[value] = this; - } - - private static ResolutionT get(int value) { - return allValues[value]; - } - } - - /** - * Enumeration representing the orientation of the picture. - */ - public static class RotationT { - - /** - * The integer value of the enumeration. - */ - public final int value; - static final RotationT[] allValues = new RotationT[2]; - /** - * Picture is right side up. - */ - public static final RotationT k0 = new RotationT(0); - /** - * Picture is rotated 180 degrees. - */ - public static final RotationT k180 = new RotationT(1); - - private RotationT(int value) { - this.value = value; - allValues[value] = this; - } - - private static RotationT get(int value) { - return allValues[value]; - } - } - - /** - * Enumeration representing the exposure priority. - */ - public static class ExposurePriorityT { - - /** - * The integer value of the enumeration. - */ - public final int value; - static final ExposurePriorityT[] allValues = new ExposurePriorityT[3]; - /** - * Prioritize image quality. - */ - public static final ExposurePriorityT imageQuality = new ExposurePriorityT(0); - /** - * No prioritization. - */ - public static final ExposurePriorityT none = new ExposurePriorityT(50); - /** - * Prioritize frame rate. - */ - public static final ExposurePriorityT frameRate = new ExposurePriorityT(100); - - private ExposurePriorityT(int value) { - this.value = value; - allValues[value / 50] = this; - } - - private static ExposurePriorityT get(int value) { - return allValues[value / 50]; - } - } - - /** - * Get a reference to the AxisCamera, or initialize the AxisCamera if it - * has not yet been initialized. If the camera is connected to the - * Ethernet switch on the robot, then this address should be 10.x.y.11 - * where x.y are your team number subnet address (same as the other IP - * addresses on the robot network). - * @param address A string containing the IP address for the camera in the - * form "10.x.y.2" for cameras on the Ethernet switch or "192.168.0.90" - * for cameras connected to the 2nd Ethernet port on an 8-slot cRIO. - * @return A reference to the AxisCamera. - */ - public static synchronized AxisCamera getInstance(String address) { - if (m_instance == null) { - m_instance = new AxisCamera(address); - // XXX: Resource Reporting Fixes -// UsageReporting.report(UsageReporting.kResourceType_AxisCamera, 2); - } - return m_instance; - } - - /** - * Get a reference to the AxisCamera, or initialize the AxisCamera if it - * has not yet been initialized. By default this will connect to a camera - * with an IP address of 10.x.y.11 with the preference that the camera be - * connected to the Ethernet switch on the robot rather than port 2 of the - * 8-slot cRIO. - * @return A reference to the AxisCamera. - */ - public static synchronized AxisCamera getInstance() { - if (m_instance == null) { - DriverStation.getInstance().waitForData(); - int teamNumber = DriverStation.getInstance().getTeamNumber(); - String address = "10."+(teamNumber/100)+"."+(teamNumber%100)+".11"; - m_instance = new AxisCamera(address); - UsageReporting.report(UsageReporting.kResourceType_AxisCamera, 1); - } - - return m_instance; - } - private static final Function cameraStartFn = NativeLibrary.getDefaultInstance().getFunction("AxisCameraStart"); - - /** - * Axis camera constructor that calls the C++ library to actually create the instance. - * @param IPAddress - */ - AxisCamera(String IPAddress) { - Pointer ptr = Pointer.createStringBuffer(IPAddress); - cameraStartFn.call1(ptr); - } - private static final TaskExecutor cameraTaskExecutor = new TaskExecutor("camera task executor"); - private static final BlockingFunction getImageFn = NativeLibrary.getDefaultInstance().getBlockingFunction("AxisCameraGetImage"); - - static { - getImageFn.setTaskExecutor(cameraTaskExecutor); - } - - /** - * Get an image from the camera. Be sure to free the image when you are done with it. - * @return A new image from the camera. - */ - public ColorImage getImage() throws AxisCameraException, NIVisionException { - ColorImage image = new HSLImage(); - if (getImageFn.call1(image.image) == 0) { - image.free(); - throw new AxisCameraException("No image available"); - } - return image; - } - // Mid-stream gets & writes - private static final Function writeBrightnessFn = NativeLibrary.getDefaultInstance().getFunction("AxisCameraWriteBrightness"); - - /** - * Write the brightness for the camera to use. - * @param brightness This must be an integer between 0 and 100, with 100 being the brightest - */ - public void writeBrightness(int brightness) { - BoundaryException.assertWithinBounds(brightness, 0, 100); - writeBrightnessFn.call1(brightness); - } - private static final Function getBrightnessFn = NativeLibrary.getDefaultInstance().getFunction("AxisCameraGetBrightness"); - - /** - * Get the current brightness of the AxisCamera - * @return An integer representing the current brightness of the axis - * camera with 0 being the darkest and 100 being the brightest. - */ - public int getBrightness() { - return getBrightnessFn.call0(); - } - private static final Function writeWhiteBalenceFn = NativeLibrary.getDefaultInstance().getFunction("AxisCameraWriteWhiteBalance"); - - /** - * Write the WhiteBalance for the camera to use. - * @param whiteBalance The value to set the white balance to on the camera. - */ - public void writeWhiteBalance(WhiteBalanceT whiteBalance) { - writeWhiteBalenceFn.call1(whiteBalance.value); - } - private static final Function getWhiteBalanceFn = NativeLibrary.getDefaultInstance().getFunction("AxisCameraGetWhiteBalance"); - - /** - * Get the white balance set on the camera. - * @return The white balance currently set on the camera. - */ - public WhiteBalanceT getWhiteBalance() { - return WhiteBalanceT.get(getWhiteBalanceFn.call0()); - } - private static final Function writeColorLevelFn = NativeLibrary.getDefaultInstance().getFunction("AxisCameraWriteColorLevel"); - - /** - * Set the color level of images returned from the camera. - * @param value This must be an integer from 0 to 100 with 0 being black and white. - */ - public void writeColorLevel(int value) { - BoundaryException.assertWithinBounds(value, 0, 100); - writeColorLevelFn.call1(value); - } - private static final Function getColorLevelFn = NativeLibrary.getDefaultInstance().getFunction("AxisCameraGetColorLevel"); - - /** - * Get the color level of images being retunred from the camera. - * @return The color level of images being returned from the camera. 0 is black and white. - */ - public int getColorLevel() { - return getColorLevelFn.call0(); - } - private static final Function writeExposureControlFn = NativeLibrary.getDefaultInstance().getFunction("AxisCameraWriteExposureControl"); - - /** - * Write the exposure mode for the camera to use. - * @param value The expsure mode for the camera to use. - */ - public void writeExposureControl(ExposureT value) { - writeExposureControlFn.call1(value.value); - } - private static final Function getExposureControlFn = NativeLibrary.getDefaultInstance().getFunction("AxisCameraGetExposureControl"); - - /** - * Get the exposure mode that the camera is using. - * @return The exposure mode that the camera is using. - */ - public ExposureT getExposureControl() { - return ExposureT.get(getExposureControlFn.call0()); - } - private static final Function writeExposurePriorityFn = NativeLibrary.getDefaultInstance().getFunction("AxisCameraWriteExposurePriority"); - - /** - * Write the exposure priority for the camera to use. - * @param value The exposure priority to use. - */ - public void writeExposurePriority(ExposurePriorityT value) { - writeExposurePriorityFn.call1(value.value); - } - private static final Function getExposurePriorityFn = NativeLibrary.getDefaultInstance().getFunction("AxisCameraGetExposurePriority"); - - /** - * Get the exposure priority that the camera is using. - * @return The exposure priority that the camera is using - */ - public ExposurePriorityT getExposurePriority() { - return ExposurePriorityT.get(getExposurePriorityFn.call0()); - } - // New-Stream gets & writes - private static final Function writeResolutionFn = NativeLibrary.getDefaultInstance().getFunction("AxisCameraWriteResolution"); - - /** - * Set the resolution of the images to return. - * @param value The resolution imaga for the camera to return. - */ - public void writeResolution(ResolutionT value) { - writeResolutionFn.call1(value.value); - } - private static final Function getResolutionFn = NativeLibrary.getDefaultInstance().getFunction("AxisCameraGetResolution"); - - /** - * Get the resolution fo the images that the camera is returning. - * @return The resolution of the images that the camera is returning. - */ - public ResolutionT getResolution() { - return ResolutionT.get(getResolutionFn.call0()); - } - private static final Function writeCompressionFn = NativeLibrary.getDefaultInstance().getFunction("AxisCameraWriteCompression"); - - /** - * Write the level of JPEG compression to use on images returned from the camera. - * @param value The level of JPEG compression to use from 0 to 100 with 0 being the least compression. - */ - public void writeCompression(int value) { - BoundaryException.assertWithinBounds(value, 0, 100); - writeCompressionFn.call1(value); - } - private static final Function getCompressionFn = NativeLibrary.getDefaultInstance().getFunction("AxisCameraGetCompression"); - - /** - * Get the compression of the images eing returned by the camera. - * @return The level of compression of the image being returned from the - * camera with 0 being the least and 100 being the greatest. - */ - public int getCompression() { - return getCompressionFn.call0(); - } - private static final Function writeRotationFn = NativeLibrary.getDefaultInstance().getFunction("AxisCameraWriteRotation"); - - /** - * Write the rotation of images for the camera to return. - * @param value The rotation to be applied to images from the camera. - */ - public void writeRotation(RotationT value) { - writeRotationFn.call1(value.value); - } - private static final Function getRotationFn = NativeLibrary.getDefaultInstance().getFunction("AxisCameraGetRotation"); - - /** - * Get the rotation of the images returned from the camera. - * @return The rotation of the images returned from the camera. - */ - public RotationT getRotation() { - return RotationT.get(getRotationFn.call0()); - } - private static final Function freshImageFn = NativeLibrary.getDefaultInstance().getFunction("AxisCameraFreshImage"); - - /** - * Has the current image from the camera been retrieved yet. - * @return true if the latest image from the camera has not been retrieved yet. - */ - public boolean freshImage() { - return freshImageFn.call0() == 0 ? false : true; - } - private static final Function getMaxFPSFn = NativeLibrary.getDefaultInstance().getFunction("AxisCameraGetMaxFPS"); - - /** - * Get the maximum frames per second that the camera will generate. - * @return the max fps. - */ - public int getMaxFPS() { - return getMaxFPSFn.call0(); - } - private static final Function writeMaxFPSFn = NativeLibrary.getDefaultInstance().getFunction("AxisCameraWriteMaxFPS"); - - /** - * Set the maximum frames per second that the camera will generate. - * @param value the new max fps - */ - public void writeMaxFPS(int value) { - writeMaxFPSFn.call1(value); - } - private static final Function deleteInstanceFn = NativeLibrary.getDefaultInstance().getFunction("AxisCameraDeleteInstance"); - - static { - VM.addShutdownHook(new Thread() { - - public void run() { - deleteInstanceFn.call0(); - } - }); - } -} diff --git a/wpilibj/wpilibJavaDevices/src/main/java/edu/wpi/first/wpilibj/camera/AxisCameraException.java b/wpilibj/wpilibJavaDevices/src/main/java/edu/wpi/first/wpilibj/camera/AxisCameraException.java deleted file mode 100644 index 9797c34f3f..0000000000 --- a/wpilibj/wpilibJavaDevices/src/main/java/edu/wpi/first/wpilibj/camera/AxisCameraException.java +++ /dev/null @@ -1,22 +0,0 @@ -/*----------------------------------------------------------------------------*/ -/* Copyright (c) FIRST 2008-2012. All Rights Reserved. */ -/* Open Source Software - may be modified and shared by FRC teams. The code */ -/* must be accompanied by the FIRST BSD license file in the root directory of */ -/* the project. */ -/*----------------------------------------------------------------------------*/ - -package edu.wpi.first.wpilibj.camera; - -/** - * An exception representing a problem with communicating with the camera. - * @author dtjones - */ -public class AxisCameraException extends Exception { - /** - * Create a new AxisCameraException. - * @param msg The message to pass with the AxisCameraException. - */ - public AxisCameraException(String msg) { - super(msg); - } -} diff --git a/wpilibj/wpilibJavaDevices/src/main/java/edu/wpi/first/wpilibj/camera/package.html b/wpilibj/wpilibJavaDevices/src/main/java/edu/wpi/first/wpilibj/camera/package.html deleted file mode 100644 index 7f85669c35..0000000000 --- a/wpilibj/wpilibJavaDevices/src/main/java/edu/wpi/first/wpilibj/camera/package.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - FRC Camera Library - - - -Provides classes for interfacing to the camera. - - - \ No newline at end of file diff --git a/wpilibj/wpilibJavaDevices/src/main/java/edu/wpi/first/wpilibj/vision/AxisCamera.java b/wpilibj/wpilibJavaDevices/src/main/java/edu/wpi/first/wpilibj/vision/AxisCamera.java new file mode 100644 index 0000000000..c69ff5c981 --- /dev/null +++ b/wpilibj/wpilibJavaDevices/src/main/java/edu/wpi/first/wpilibj/vision/AxisCamera.java @@ -0,0 +1,556 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) FIRST 2014. All Rights Reserved. */ +/* Open Source Software - may be modified and shared by FRC teams. The code */ +/* must be accompanied by the FIRST BSD license file in the root directory of */ +/* the project. */ +/*----------------------------------------------------------------------------*/ + +package edu.wpi.first.wpilibj.vision; + +import edu.wpi.first.wpilibj.image.ColorImage; +import edu.wpi.first.wpilibj.image.HSLImage; +import edu.wpi.first.wpilibj.image.NIVisionException; + +import java.io.DataInputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.nio.ByteBuffer; + +import static com.ni.vision.NIVision.Image; +import static com.ni.vision.NIVision.Priv_ReadJPEGString_C; +import static edu.wpi.first.wpilibj.Timer.delay; + +/** + * Axis M1011 network camera + */ +public class AxisCamera { + public enum WhiteBalance { + kAutomatic, + kHold, + kFixedOutdoor1, + kFixedOutdoor2, + kFixedIndoor, + kFixedFluorescent1, + kFixedFluorescent2, + } + + public enum ExposureControl { + kAutomatic, + kHold, + kFlickerFree50Hz, + kFlickerFree60Hz, + } + + public enum Resolution { + k640x480, + k480x360, + k320x240, + k240x180, + k176x144, + k160x120, + } + + public enum Rotation { + k0, k180 + } + + private static final String[] kWhiteBalanceStrings = + {"auto", "hold", "fixed_outdoor1", "fixed_outdoor2", "fixed_indoor", + "fixed_fluor1", "fixed_fluor2",}; + + private static final String[] kExposureControlStrings = + {"auto", "hold", "flickerfree50", "flickerfree60",}; + + private static final String[] kResolutionStrings = + {"640x480", "480x360", "320x240", "240x180", "176x144", "160x120",}; + + private static final String[] kRotationStrings = + {"0", "180",}; + + private final static int kImageBufferAllocationIncrement = 1000; + + private String m_cameraHost; + private Socket m_cameraSocket; + + private ByteBuffer m_imageData = ByteBuffer.allocate(5000); + private final Object m_imageDataLock = new Object(); + private boolean m_freshImage = false; + + private int m_brightness = 50; + private WhiteBalance m_whiteBalance = WhiteBalance.kAutomatic; + private int m_colorLevel = 50; + private ExposureControl m_exposureControl = ExposureControl.kAutomatic; + private int m_exposurePriority = 50; + private int m_maxFPS = 0; + private Resolution m_resolution = Resolution.k640x480; + private int m_compression = 50; + private Rotation m_rotation = Rotation.k0; + private final Object m_parametersLock = new Object(); + private boolean m_parametersDirty = true; + private boolean m_streamDirty = true; + + private boolean m_done = false; + + /** + * AxisCamera constructor + * + * @param cameraHost The host to find the camera at, typically an IP address + */ + public AxisCamera(String cameraHost) { + m_cameraHost = cameraHost; + m_captureThread.start(); + } + + /** + * Return true if the latest image from the camera has not been retrieved by calling GetImage() yet. + * @return true if the image has not been retrieved yet. + */ + public boolean isFreshImage() { + return m_freshImage; + } + + /** + * Get an image from the camera and store it in the provided image. + * + * @param image The imaq image to store the result in. This must be an HSL or RGB image. + * @return true upon success, false on a failure + */ + public boolean getImage(Image image) { + if (m_imageData.limit() == 0) { + return false; + } + + synchronized (m_imageDataLock) { + Priv_ReadJPEGString_C(image, m_imageData.array()); + } + + m_freshImage = false; + + return true; + } + + /** + * Get an image from the camera and store it in the provided image. + * + * @param image The image to store the result in. This must be an HSL or RGB image + * @return true upon success, false on a failure + */ + public boolean getImage(ColorImage image) { + return this.getImage(image.image); + } + + /** + * Instantiate a new image object and fill it with the latest image from the camera. + * + * @return a pointer to an HSLImage object + */ + public HSLImage getImage() throws NIVisionException { + HSLImage image = new HSLImage(); + this.getImage(image); + return image; + } + + /** + * Request a change in the brightness of the camera images. + * + * @param brightness valid values 0 .. 100 + */ + public void writeBrightness(int brightness) { + if (brightness < 0 || brightness > 100) { + throw new IllegalArgumentException("Brightness must be from 0 to 100"); + } + + synchronized (m_parametersLock) { + if (m_brightness != brightness) { + m_brightness = brightness; + m_parametersDirty = true; + } + } + } + + /** + * @return The configured brightness of the camera images + */ + public int getBrightness() { + synchronized (m_parametersLock) { + return m_brightness; + } + } + + /** + * Request a change in the white balance on the camera. + * + * @param whiteBalance Valid values from the WhiteBalance enum. + */ + public void writeWhiteBalance(WhiteBalance whiteBalance) { + synchronized (m_parametersLock) { + if (m_whiteBalance != whiteBalance) { + m_whiteBalance = whiteBalance; + m_parametersDirty = true; + } + } + } + + /** + * @return The configured white balances of the camera images + */ + public WhiteBalance getWhiteBalance() { + synchronized (m_parametersLock) { + return m_whiteBalance; + } + } + + /** + * Request a change in the color level of the camera images. + * + * @param colorLevel valid values are 0 .. 100 + */ + public void writeColorLevel(int colorLevel) { + if (colorLevel < 0 || colorLevel > 100) { + throw new IllegalArgumentException("Color level must be from 0 to 100"); + } + + synchronized (m_parametersLock) { + if (m_colorLevel != colorLevel) { + m_colorLevel = colorLevel; + m_parametersDirty = true; + } + } + } + + /** + * @return The configured color level of the camera images + */ + public int getColorLevel() { + synchronized (m_parametersLock) { + return m_colorLevel; + } + } + + /** + * Request a change in the camera's exposure mode. + * + * @param exposureControl A mode to write in the Exposure enum. + */ + public void writeExposureControl(ExposureControl exposureControl) { + synchronized (m_parametersLock) { + if (m_exposureControl != exposureControl) { + m_exposureControl = exposureControl; + m_parametersDirty = true; + } + } + } + + /** + * @return The configured exposure control mode of the camera + */ + public ExposureControl getExposureControl() { + synchronized (m_parametersLock) { + return m_exposureControl; + } + } + + /** + * Request a change in the exposure priority of the camera. + * + * @param exposurePriority Valid values are 0, 50, 100. + * 0 = Prioritize image quality + * 50 = None + * 100 = Prioritize frame rate + */ + public void writeExposurePriority(int exposurePriority) { + if (exposurePriority != 0 && exposurePriority != 50 && exposurePriority != 100) { + throw new IllegalArgumentException("Exposure priority must be 0, 50, or 100"); + } + + synchronized (m_parametersLock) { + if (m_exposurePriority != exposurePriority) { + m_exposurePriority = exposurePriority; + m_parametersDirty = true; + } + } + } + + /** + * @return The configured exposure priority of the camera + */ + public int getExposurePriority() { + synchronized (m_parametersLock) { + return m_exposurePriority; + } + } + + /** + * Write the maximum frames per second that the camera should send + * Write 0 to send as many as possible. + * + * @param maxFPS The number of frames the camera should send in a second, exposure permitting. + */ + public void writeMaxFPS(int maxFPS) { + synchronized (m_parametersLock) { + if (m_maxFPS != maxFPS) { + m_maxFPS = maxFPS; + m_parametersDirty = true; + m_streamDirty = true; + } + } + } + + /** + * @return The configured maximum FPS of the camera + */ + public int getMaxFPS() { + synchronized (m_parametersLock) { + return m_maxFPS; + } + } + + /** + * Write resolution value to camera. + * + * @param resolution The camera resolution value to write to the camera. + */ + public void writeResolution(Resolution resolution) { + synchronized (m_parametersLock) { + if (m_resolution != resolution) { + m_resolution = resolution; + m_parametersDirty = true; + m_streamDirty = true; + } + } + } + + /** + * @return The configured resolution of the camera (not necessarily the same + * resolution as the most recent image, if it was changed recently.) + */ + public Resolution getResolution() { + synchronized (m_parametersLock) { + return m_resolution; + } + } + + /** + * Write the compression value to the camera. + * + * @param compression Values between 0 and 100. + */ + public void writeCompression(int compression) { + if (compression < 0 || compression > 100) { + throw new IllegalArgumentException("Compression must be from 0 to 100"); + } + + synchronized (m_parametersLock) { + if (m_compression != compression) { + m_compression = compression; + m_parametersDirty = true; + m_streamDirty = true; + } + } + } + + /** + * @return The configured compression level of the camera images + */ + public int getCompression() { + synchronized (m_parametersLock) { + return m_compression; + } + } + + /** + * Write the rotation value to the camera. + * If you mount your camera upside down, use this to adjust the image for you. + * + * @param rotation A value from the {@link Rotation} enum + */ + public void writeRotation(Rotation rotation) { + synchronized (m_parametersLock) { + if (m_rotation != rotation) { + m_rotation = rotation; + m_parametersDirty = true; + m_streamDirty = true; + } + } + } + + /** + * @return The configured rotation mode of the camera + */ + public Rotation getRotation() { + synchronized (m_parametersLock) { + return m_rotation; + } + } + + /** + * Thread spawned by AxisCamera constructor to receive images from cam + */ + private Thread m_captureThread = new Thread(new Runnable() { + @Override + public void run() { + int consecutiveErrors = 0; + + // Loop on trying to setup the camera connection. This happens in a background + // thread so it shouldn't effect the operation of user programs. + while (!m_done) { + String requestString = "GET /mjpg/video.mjpg HTTP/1.1\n" + + "User-Agent: HTTPStreamClient\n" + + "Connection: Keep-Alive\n" + + "Cache-Control: no-cache\n" + + "Authorization: Basic RlJDOkZSQw==\n\n"; + + try { + m_cameraSocket = AxisCamera.this.createCameraSocket(requestString); + AxisCamera.this.readImagesFromCamera(); + consecutiveErrors = 0; + } catch (IOException e) { + consecutiveErrors++; + + if (consecutiveErrors > 5) { + e.printStackTrace(); + } + } + + delay(0.5); + } + } + }); + + /** + * This function actually reads the images from the camera. + */ + private void readImagesFromCamera() throws IOException { + DataInputStream cameraInputStream = new DataInputStream(m_cameraSocket.getInputStream()); + + while (!m_done) { + String line = cameraInputStream.readLine(); + + if (line.startsWith("Content-Length: ")) { + int contentLength = Integer.valueOf(line.substring(16)); + + /* Skip the next blank line */ + cameraInputStream.readLine(); + contentLength -= 4; + + /* The next four bytes are the JPEG magic number */ + byte[] data = new byte[contentLength]; + cameraInputStream.readFully(data); + + synchronized (m_imageDataLock) { + if (m_imageData.capacity() < data.length) { + m_imageData = ByteBuffer.allocate(data.length + kImageBufferAllocationIncrement); + } + + m_imageData.clear(); + m_imageData.limit(contentLength); + m_imageData.put(data); + + m_freshImage = true; + } + + if (this.writeParameters()) { + break; + } + + /* Skip the boundary and Content-Type header */ + cameraInputStream.readLine(); + cameraInputStream.readLine(); + } + } + + m_cameraSocket.close(); + } + + /** + * Send a request to the camera to set all of the parameters. This is called + * in the capture thread between each frame. This strategy avoids making lots + * of redundant HTTP requests, accounts for failed initial requests, and + * avoids blocking calls in the main thread unless necessary. + *

+ * This method does nothing if no parameters have been modified since it last + * completely successfully. + * + * @return true if the stream should be restarted due to a + * parameter changing. + */ + private boolean writeParameters() { + if (m_parametersDirty) { + String request = "GET /axis-cgi/admin/param.cgi?action=update"; + + synchronized (m_parametersLock) { + request += "&ImageSource.I0.Sensor.Brightness=" + m_brightness; + request += "&ImageSource.I0.Sensor.WhiteBalance=" + kWhiteBalanceStrings[m_whiteBalance.ordinal()]; + request += "&ImageSource.I0.Sensor.ColorLevel=" + m_colorLevel; + request += "&ImageSource.I0.Sensor.Exposure=" + kExposureControlStrings[m_exposureControl.ordinal()]; + request += "&ImageSource.I0.Sensor.ExposurePriority=" + m_exposurePriority; + request += "&Image.I0.Stream.FPS=" + m_maxFPS; + request += "&Image.I0.Appearance.Resolution=" + kResolutionStrings[m_resolution.ordinal()]; + request += "&Image.I0.Appearance.Compression=" + m_compression; + request += "&Image.I0.Appearance.Rotation=" + kRotationStrings[m_rotation.ordinal()]; + } + + request += " HTTP/1.1\n"; + request += "User-Agent: HTTPStreamClient\n"; + request += "Connection: Keep-Alive\n"; + request += "Cache-Control: no-cache\n"; + request += "Authorization: Basic RlJDOkZSQw==\n\n"; + + try { + Socket socket = this.createCameraSocket(request); + socket.close(); + + m_parametersDirty = false; + + if (m_streamDirty) { + m_streamDirty = false; + return true; + } else { + return false; + } + } catch (IOException | NullPointerException e) { + return false; + } + + } + + return false; + } + + /** + * Create a socket connected to camera + * Used to create a connection for reading images and setting parameters + * + * @param requestString The initial request string to send upon successful connection. + * @return The created socket + */ + private Socket createCameraSocket(String requestString) throws IOException { + /* Connect to the server */ + Socket socket = new Socket(); + socket.connect(new InetSocketAddress(m_cameraHost, 80), 5000); + + /* Send the HTTP headers */ + OutputStream socketOutputStream = socket.getOutputStream(); + socketOutputStream.write(requestString.getBytes()); + + return socket; + } + + @Override + public String toString() { + return "AxisCamera{" + + "FreshImage=" + isFreshImage() + + ", Brightness=" + getBrightness() + + ", WhiteBalance=" + getWhiteBalance() + + ", ColorLevel=" + getColorLevel() + + ", ExposureControl=" + getExposureControl() + + ", ExposurePriority=" + getExposurePriority() + + ", MaxFPS=" + getMaxFPS() + + ", Resolution=" + getResolution() + + ", Compression=" + getCompression() + + ", Rotation=" + getRotation() + + '}'; + } +} diff --git a/wpilibj/wpilibJavaJNI/lib/NIVisionJNI.cpp b/wpilibj/wpilibJavaJNI/lib/NIVisionJNI.cpp index 6793315535..26b29ba5f8 100644 --- a/wpilibj/wpilibJavaJNI/lib/NIVisionJNI.cpp +++ b/wpilibj/wpilibJavaJNI/lib/NIVisionJNI.cpp @@ -4890,6 +4890,17 @@ JNIEXPORT jlong JNICALL Java_com_ni_vision_NIVision__1imaqRake(JNIEnv* env, jcla return (jlong)rv; } +/* J: void Priv_ReadJPEGString_C(Image image, byte[] string) + * JN: void Priv_ReadJPEGString_C(long image, long string, int stringLength) + * C: int Priv_ReadJPEGString_C(Image* image, const unsigned char* string, unsigned int stringLength) + */ + +JNIEXPORT void JNICALL Java_com_ni_vision_NIVision__1Priv_1ReadJPEGString_1C(JNIEnv* env, jclass , jlong image, jlong string, jint stringLength) +{ + int rv = Priv_ReadJPEGString_C((Image*)image, (const unsigned char*)string, (unsigned int)stringLength); + if (rv == 0) throwJavaException(env); +} + /* * Purpose : Include file for NI-IMAQdx library support. */ diff --git a/wpilibj/wpilibJavaJNI/nivision/nivision_2011.ini b/wpilibj/wpilibJavaJNI/nivision/nivision_2011.ini index 848cd21007..025d2f18ba 100644 --- a/wpilibj/wpilibJavaJNI/nivision/nivision_2011.ini +++ b/wpilibj/wpilibJavaJNI/nivision/nivision_2011.ini @@ -820,6 +820,9 @@ retarraysize=numBarcodes retarraysize=numMatches [imaqColorHistogram] nullok=mask +[Priv_ReadJPEGString_C] +arraysize=string:stringLength +inparams=image,string ; block comment exclusion list [Block Comment]