Binary operator '+=' cannot be applied to operands of type 'Int' and 'UInt8' - objective-c

Translating Obj-C to Swift. As you can see I declared let buf = UnsafeMutablePointer<UInt8>(CVPixelBufferGetBaseAddress(cvimgRef)) so I'm getting the error in the for loop below it.
Binary operator '+=' cannot be applied to operands of type 'Int' and 'UInt8'
Also as a little addendum I don't know how to translate the remaining Obj-C code below the for loop. What does that slash mean and how do I deal with the pointer? I have to say UnsafeMutableFloat somewhere?
// process the frame of video
func captureOutput(captureOutput:AVCaptureOutput, didOutputSampleBuffer sampleBuffer:CMSampleBuffer, fromConnection connection:AVCaptureConnection) {
// if we're paused don't do anything
if currentState == CurrentState.statePaused {
// reset our frame counter
self.validFrameCounter = 0
return
}
// this is the image buffer
var cvimgRef:CVImageBufferRef = CMSampleBufferGetImageBuffer(sampleBuffer)
// Lock the image buffer
CVPixelBufferLockBaseAddress(cvimgRef, 0)
// access the data
var width: size_t = CVPixelBufferGetWidth(cvimgRef)
var height:size_t = CVPixelBufferGetHeight(cvimgRef)
// get the raw image bytes
let buf = UnsafeMutablePointer<UInt8>(CVPixelBufferGetBaseAddress(cvimgRef))
var bprow: size_t = CVPixelBufferGetBytesPerRow(cvimgRef)
var r = 0
var g = 0
var b = 0
for var y = 0; y < height; y++ {
for var x = 0; x < width * 4; x += 4 {
b += buf[x]; g += buf[x + 1]; r += buf[x + 2] // error
}
buf += bprow() // error
}
Remaining Obj-C code.
r/=255*(float) (width*height);
g/=255*(float) (width*height);
b/=255*(float) (width*height);

You have a lot of type mismatch error.
The type of x should not be UInt8 because x to increase until the value of the width.
for var x:UInt8 = 0; x < width * 4; x += 4 { // error: '<' cannot be applied to operands of type 'UInt8' and 'Int'
So fix it like below:
for var x = 0; x < width * 4; x += 4 {
To increment the pointer address, you can use advancedBy() function.
buf += bprow(UnsafeMutablePointer(UInt8)) // error: '+=' cannot be applied to operands of type 'UnsafeMutablePointer<UInt8>' and 'size_t'
Like below:
var pixel = buf.advancedBy(y * bprow)
And this line,
RGBtoHSV(r, g, b) // error
There are no implicit casts in Swift between CGFloat and Float unfortunately. So you should cast explicitly to CGFloat.
RGBtoHSV(CGFloat(r), g: CGFloat(g), b: CGFloat(b))
The whole edited code is here:
func RGBtoHSV(r: CGFloat, g: CGFloat, b: CGFloat) -> (h: CGFloat, s: CGFloat, v: CGFloat) {
var h: CGFloat = 0.0
var s: CGFloat = 0.0
var v: CGFloat = 0.0
let col = UIColor(red: r, green: g, blue: b, alpha: 1.0)
col.getHue(&h, saturation: &s, brightness: &v, alpha: nil)
return (h, s, v)
}
// process the frame of video
func captureOutput(captureOutput:AVCaptureOutput, didOutputSampleBuffer sampleBuffer:CMSampleBuffer, fromConnection connection:AVCaptureConnection) {
// if we're paused don't do anything
if currentState == CurrentState.statePaused {
// reset our frame counter
self.validFrameCounter = 0
return
}
// this is the image buffer
var cvimgRef = CMSampleBufferGetImageBuffer(sampleBuffer)
// Lock the image buffer
CVPixelBufferLockBaseAddress(cvimgRef, 0)
// access the data
var width = CVPixelBufferGetWidth(cvimgRef)
var height = CVPixelBufferGetHeight(cvimgRef)
// get the raw image bytes
let buf = UnsafeMutablePointer<UInt8>(CVPixelBufferGetBaseAddress(cvimgRef))
var bprow = CVPixelBufferGetBytesPerRow(cvimgRef)
var r: Float = 0.0
var g: Float = 0.0
var b: Float = 0.0
for var y = 0; y < height; y++ {
var pixel = buf.advancedBy(y * bprow)
for var x = 0; x < width * 4; x += 4 { // error: '<' cannot be applied to operands of type 'UInt8' and 'Int'
b += Float(pixel[x])
g += Float(pixel[x + 1])
r += Float(pixel[x + 2])
}
}
r /= 255 * Float(width * height)
g /= 255 * Float(width * height)
b /= 255 * Float(width * height)
//}
// convert from rgb to hsv colourspace
var h: Float = 0.0
var s: Float = 0.0
var v: Float = 0.0
RGBtoHSV(CGFloat(r), g: CGFloat(g), b: CGFloat(b)) // error
}

Related

SVGPointList length changes using svg.js

Hi I am using the function createPoint to animate a polygon using gsap. I am also using svg.js
If I use vanilla javascript to get the points of the svg with
var polygon = document.querySelector("polygon");
var points = polygon.points;
it returns 3 points which correspond to the number of times the createPoint function is run. This logs out as:
0: SVGPoint {x: 105.30396270751953, y: 143.0928955078125}
1: SVGPoint {x: 348.09027099609375, y: 97.7249984741211}
2: SVGPoint {x: 276.54010009765625, y: 327.56372070}
If I use the svg.js code
const draw = SVG().addTo('body')
var svg = draw.node;
const polygon = draw.polygon().node;
var points = polygon.points;
the same function logs a list of 4 SVGPoints with the first point being {x:0,y:0} even though I am only running the function 3 times. Where is the additional (index 0) svg point coming from? Thanks in advance
0: SVGPoint {x: 0, y: 0}
1: SVGPoint {x: 93.79865264892578, y: 124.19292449951172}
2: SVGPoint {x: 346.3572082519531, y: 97.5942153930664}
3: SVGPoint {x: 227.08517456054688, y: 269.97042846
given the following html
<svg>
<polygon points="">
</svg>
And the code below
TweenLite.defaultEase = Sine.easeInOut;
const draw = SVG().addTo('body')
var svg = draw.node;
const polygon = draw.polygon().node;
var points = polygon.points;
console.log('points',points)
var offset = 75;
createPoint(100, 100);
createPoint(300, 100);
createPoint(300, 300);
// createPoint(100, 300);
function createPoint(x, y) {
var minX = x - offset;
var maxX = x + offset;
var minY = y - offset;
var maxY = y + offset;
var point = points.appendItem(svg.createSVGPoint());
point.x = x;
point.y = y;
moveX();
moveY();
function moveX() {
TweenLite.to(point, random(2, 4), {
x: random(minX, maxX),
delay: random(0.5),
onComplete: moveX
});
}
function moveY() {
TweenLite.to(point, random(2, 4), {
y: random(minY, maxY),
delay: random(0.5),
onComplete: moveY
});
}
}
function random(min, max) {
if (max == null) { max = min; min = 0; }
if (min > max) { var tmp = min; min = max; max = tmp; }
return min + (max - min) * Math.random();
}
Just found out that by adding an empty array to the polygon i.e
const polygon = draw.polygon([]).node
it removes the default {x:0,y:0} object. Dont ask me why :)

Attempt to Crop bitmap using Renderscript failed with invalid slot index error

I'm trying to apply the solution presented in this Question's answer to crop a bitmap using RenderSript. My approach is a little bit different since I'm using bitmap directly as an input to crop.
Having very limited knowledge in RenderScript I developed the below code to crop my bitmap by modifying that answer.
crop.rs
#pragma version(1)
#pragma rs java_package_name(com.xxx.yyy)
#pragma rs_fp_relaxed
int32_t width;
int32_t height;
rs_allocation croppedImg;
uint xStart, yStart;
void __attribute__((kernel)) doCrop(uchar4 in,uint32_t x, uint32_t y) {
rsSetElementAt_uchar4(croppedImg,in, x-xStart, y-yStart);
}
ImageCropper
import android.content.Context
import android.graphics.Bitmap
import androidx.renderscript.*
import com.xxx.yyy.ScriptC_crop
import kotlin.math.abs
class ImageCropper(context: Context?) {
val rs = RenderScript.create(context)
var dx = 0 // (-width < dx < width);
var dy = 250 // (- height < dy < height);
var xStart = 50
var xEnd = 100
var yStart = 50
var yEnd = 100
fun crop(sourceBitmap: Bitmap): Bitmap{
val width = sourceBitmap.width
val height = sourceBitmap.height
if (dx<0) {
xStart = abs(dx)
xEnd= width
} else {
xStart = 0
xEnd = width - abs(dx)
}
if (dy<0) {
yStart= abs(dy)
yEnd=height
} else {
yStart = 0;
yEnd = height - abs(dy)
}
val cropperScript = ScriptC_crop(rs)
val inputType = Type.createXY(rs, Element.RGBA_8888(rs), width, height)
val inputAllocation = Allocation.createTyped(rs, inputType, Allocation.USAGE_SCRIPT)
inputAllocation.copyFrom(sourceBitmap)
val outputType = Type.createXY(rs, Element.RGBA_8888(rs), xEnd - xStart, yEnd - yStart)
val outputAllocation = Allocation.createTyped(rs, outputType, Allocation.USAGE_SCRIPT)
cropperScript._croppedImg = outputAllocation
cropperScript._width = width
cropperScript._height = height
cropperScript._xStart = xStart.toLong()
cropperScript._yStart = yStart.toLong()
val launchOptions: Script.LaunchOptions = Script.LaunchOptions()
launchOptions.setX(xStart, xEnd)
launchOptions.setY(yStart, yEnd)
cropperScript.forEach_doCrop(inputAllocation, launchOptions)
val resultBitmap = Bitmap.createBitmap(xEnd - xStart, yEnd - yStart, sourceBitmap.config)
outputAllocation.copyTo(resultBitmap)
rs.destroy()
return resultBitmap
}
}
When I try to execute the code I get a bellow error.
2021-04-11 20:55:41.639 18145-18202/com.chathuranga.shan.renderscriptexample E/RenderScript: Script::setVar unable to set allocation, invalid slot index
2021-04-11 20:55:41.645 18145-18202/com.chathuranga.shan.renderscriptexample A/libc: Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x519 in tid 18202 (erscriptexample), pid 18145 (erscriptexample)
2021-04-11 20:55:41.639 18145-18202/com.chathuranga.shan.renderscriptexample E/RenderScript: Script::setVar unable to set allocation, invalid slot index
This error occurs in the line where I set _xStart and _yStart values. What am I doing wrong? is it some sort of data type issue?

Assignment problem - What am I doing wrong?

I am working on image seam carving project, looking for some help. Can someone tell me what am I doing wrong here, Hyperskill is not accepting my solution. I am pretty sure I did not understand the project statement correctly. (I’ve been fighting it for a week)
Project: https://hyperskill.org/projects/100/stages/553/implement
First I am finding the minimum seam from all possible seams.
var minX = 0
// (minSeamX, 0) would be the coordinate of the minimum seam
var minSeamX = 0
var minSeam = Double.MAX_VALUE
//Starting from top left find the sum of pixel energies for all possible seams(#width number of possible seams)
for (column in 0 until width) {
var totalSeam = 0.0
var xHigh = column
var xLow = column
var min = Double.MAX_VALUE
for (y in 0 until height) {
for (x in xLow..xHigh) {
if (x < 0 || x > width - 1) continue
val energy = calculateEnergy(x, y, bufferedImage)
// println("Energy $x $y $energy")
if (energy < min) {
min = energy
minX = x
}
}
totalSeam += min
min = Double.MAX_VALUE
xLow = minX - 1
xHigh = minX + 1
}
if (totalSeam < minSeam) {
minSeamX = column
minSeam = totalSeam
}
println("total:$totalSeam")
}
after that I am applying the color to the minimum seam pixels
var xLow = minSeamX
var xHigh = minSeamX
var min = Double.MAX_VALUE
for (y in 0 until height) {
for (x in xLow..xHigh) {
val energy = calculateEnergy(x, y, bufferedImage)
if (energy < min) {
min = energy
minX = x
}
}
val createGraphics = applyColor(outImage, minX, y)
min = Double.MAX_VALUE
xLow = minX - 1
xHigh = minX + 1
}
Complete code
package seamcarving
import java.awt.Color
import java.awt.Graphics2D
import java.awt.image.BufferedImage
import java.io.File
import javax.imageio.ImageIO
import kotlin.math.pow
import kotlin.math.sqrt
fun main(args: Array<String>) {
val bufferedImage = ImageIO.read(File("/Users/name/Downloads/images/blue.png"))
val outImage = ImageIO.read(File("/Users/name/Downloads/images/blue.png"))
val height = bufferedImage.height
val width = bufferedImage.width
var minX = 0
// (minSeamX, 0) would be the coordinate of the minimum seam
var minSeamX = 0
var minSeam = Double.MAX_VALUE
//Starting from top left find the sum of pixel energies for all possible seams(#width number of possible seams)
for (column in 0 until width) {
var totalSeam = 0.0
var xHigh = column
var xLow = column
var min = Double.MAX_VALUE
for (y in 0 until height) {
for (x in xLow..xHigh) {
if (x < 0 || x > width - 1) continue
val energy = calculateEnergy(x, y, bufferedImage)
// println("Energy $x $y $energy")
if (energy < min) {
min = energy
minX = x
}
}
totalSeam += min
min = Double.MAX_VALUE
xLow = minX - 1
xHigh = minX + 1
}
if (totalSeam < minSeam) {
minSeamX = column
minSeam = totalSeam
}
println("total:$totalSeam")
}
var xLow = minSeamX
var xHigh = minSeamX
var min = Double.MAX_VALUE
for (y in 0 until height) {
for (x in xLow..xHigh) {
val energy = calculateEnergy(x, y, bufferedImage)
if (energy < min) {
min = energy
minX = x
}
}
val createGraphics = applyColor(outImage, minX, y)
min = Double.MAX_VALUE
xLow = minX - 1
xHigh = minX + 1
}
// for (x in 0 until width) {
// for (y in 0 until height) {
// val intensity = ((255.0 * array[x][y]) / max).toInt()
// val color = Color(intensity, intensity, intensity)
//// outputImage.setRGB(x, y, intensity)
// createGraphics.paint = color
// createGraphics.fillRect(x, y, 1, 1)
// }
// }
ImageIO.write(outImage, "png", File("out.png"))
// ImageIO.write(bufferedImage, "png", File("${args[3]}"))
}
private fun applyColor(outputImage: BufferedImage, maxX: Int, maxY: Int): Graphics2D? {
val createGraphics = outputImage.createGraphics()
val color = Color(255, 0, 0)
createGraphics.paint = color
createGraphics.fillRect(maxX, maxY, 1, 1)
return createGraphics
}
private fun calculateEnergy(x: Int, y: Int, bufferedImage: BufferedImage): Double {
return sqrt(getXGradient(x, y, bufferedImage) + getYGradient(x, y, bufferedImage))
}
fun getXGradient(x: Int, y: Int, inImage: BufferedImage): Double {
val width = inImage.width
var xx = x
var yy = y
if (x == 0) xx = 1
if (x == width - 1) xx = x - 1
val lc = Color(inImage.getRGB(xx - 1, yy))
val rc = Color(inImage.getRGB(xx + 1, yy))
return (lc.red - rc.red).toDouble().pow(2.0) + (lc.green - rc.green).toDouble().pow(2.0) + (lc.blue - rc.blue).toDouble().pow(2.0)
}
fun getYGradient(x: Int, y: Int, inImage: BufferedImage): Double {
val height = inImage.height
var xx = x
var yy = y
if (y == 0) yy = 1
if (y == height - 1) yy = y - 1
val lc = Color(inImage.getRGB(xx, yy - 1))
val rc = Color(inImage.getRGB(xx, yy + 1))
return (lc.red - rc.red).toDouble().pow(2.0) + (lc.green - rc.green).toDouble().pow(2.0) + (lc.blue - rc.blue).toDouble().pow(2.0)
}

Translating Obj-C RGBtoHSV() function to Swift. min = MIN( r, MIN(g, b )); etc

Could someone please help me with this function? This function is inside of a camera app that uses a filter algorithm to detect differences in colour variants etc. The syntax is very difficult for me. I don't know how to deal with the pointers in the arguments, the min and max variable syntax, what is delta etc? Could someone please translate for me? Much appreciated.
Also should I be doing something with UIColor? Someone mentioned it below. I have no idea how to convert though.
// r,g,b values are from 0 to 1 // h = [0,360], s = [0,1], v = [0,1]
// if s == 0, then h = -1 (undefined)
void RGBtoHSV( float r, float g, float b, float *h, float *s, float *v ) {
float min, max, delta;
min = MIN( r, MIN(g, b ));
max = MAX( r, MAX(g, b ));
*v = max;
delta = max - min;
if( max != 0 )
*s = delta / max;
else {
// r = g = b = 0
*s = 0;
*h = -1;
return;
}
if( r == max )
*h = ( g - b ) / delta;
else if( g == max )
*h=2+(b-r)/delta;
else
*h=4+(r-g)/delta;
*h *= 60;
if( *h < 0 )
*h += 360;
}
Swift translation attempt
// r,g,b values are from 0 to 1 // h = [0,360], s = [0,1], v = [0,1]
// if s == 0, then h = -1 (undefined)
func RGBtoHSV(r:Float, g:Float, b:Float, h:Float, s:Float, v:Float) {
var min:Float = 0.0
var max:Float = 0.0
var delta:Float = 0.0
min = MIN(r, MIN(g, b))
max = MAX(r, MAX(g, b))
var v = max
delta = max - min
if max != 0 {
var s = delta / max
}
else{
// r = g = b = 0
var s = 0
var h = -1
return
}
if r == max {
var h = (g - b) / delta
}
else if (g == max) {
var h = 2 + (b - r ) / delta
}
else{
var h = 4 + (r - g) / delta
var h = 60
}
if (h < 0) {
var h += 360 // how to deal with all of these pointers here in the original Obj-C function above?
}
}
The Swift equivalent of passing a pointer (the address of a variable)
is an "inout parameter":
func RGBtoHSV(r : Float, g : Float, b : Float, inout h : Float, inout s : Float, inout v : Float) {
let rgbMin = min(r, g, b)
let rgbMax = max(r, g, b)
let delta = rgbMax - rgbMin
v = rgbMax
s = delta/rgbMax
h = Float(0.0) // Replace by your actual calculation
}
This would be called as
let r : Float = 0.3
let g : Float = 0.5
let b : Float = 0.7
var h : Float = 0.0
var s : Float = 0.0
var v : Float = 0.0
RGBtoHSV(r, g, b, &h, &s, &v)
println([h, s, v])
But in Swift there is a better way to return multiple values:
You can return a tuple:
func RGBtoHSV(r : Float, g : Float, b : Float) -> (h : Float, s : Float, v : Float) {
let rgbMin = min(r, g, b)
let rgbMax = max(r, g, b)
let delta = rgbMax - rgbMin
let v = rgbMax
let s = delta/rgbMax
let h = Float(0.0) // Replace by your actual calculation
return (h, s, v)
}
And this would be called as
let r : Float = 0.3
let g : Float = 0.5
let b : Float = 0.7
let (h, s, v) = RGBtoHSV(r, g, b)
println([h, s, v])
Note that on iOS you can simply use the UIColor class to convert
between RGB and HSV (== HSB):
func RGBtoHSV(r : CGFloat, g : CGFloat, b : CGFloat) -> (h : CGFloat, s : CGFloat, v : CGFloat) {
var h : CGFloat = 0.0
var s : CGFloat = 0.0
var v : CGFloat = 0.0
let col = UIColor(red: r, green: g, blue: b, alpha: 1.0)
col.getHue(&h, saturation: &s, brightness: &v, alpha: nil)
return (h, s, v)
}
(or use the various extension/convenience methods from
What is the best/shortest way to convert a UIColor to hex (web color) in Swift?)

Is there any PDF command, that scales rectangle coordinates?

I have an application, that extracts text and rectangles from pdf files for further analysis. I use ItextSharp for extraction, and everything worked smoothly, until I stumbled upon a document, which has some strange table cell rectangles. The values in the drawing commands, that I retrieve, seem 10 times larger, than actual dimensions of the latter rectangles.
Just an example :
2577 831.676 385.996 3.99609 re
At the same time, when viewing the document all rectangles seem to correctly fit in the bounds of document pages. My guess is that there should be some scaling command, telling, that these values should be scaled down. Is the assumption right, or how is it possible, that such large rectangles are rendered so, that they stay inside the bounds of a page ?
The pdf document is behind this link : https://www.dropbox.com/s/gyvon0dwk6a9cj0/prEVS_ISO_11620_KOM_et.pdf?dl=0
The code, that handles extraction of dimensions from PRStream is as follows :
private static List<PdfRect> GetRectsAndLinesFromStream(PRStream stream)
{
var streamBytes = PdfReader.GetStreamBytes(stream);
var tokenizer = new PRTokeniser(new RandomAccessFileOrArray(streamBytes));
List<string> newBuf = new List<string>();
List<PdfRect> rects = new List<PdfRect>();
List<string> allTokens = new List<string>();
float[,] ctm = null;
List<float[,]> ctms = new List<float[,]>();
//if current ctm has not yet been added to list
bool pendingCtm = false;
//format definition for string-> float conversion
var format = new System.Globalization.NumberFormatInfo();
format.NegativeSign = "-";
while (tokenizer.NextToken())
{
//Add them to our master buffer
newBuf.Add(tokenizer.StringValue);
if (
tokenizer.TokenType == PRTokeniser.TokType.OTHER && newBuf[newBuf.Count - 1] == "re"
)
{
float startPointX = (float)double.Parse(newBuf[newBuf.Count - 5], format);
float startPointY = (float)double.Parse(newBuf[newBuf.Count - 4], format);
float width = (float)double.Parse(newBuf[newBuf.Count - 3], format);
float height = (float)double.Parse(newBuf[newBuf.Count - 2], format);
float endPointX = startPointX + width;
float endPointY = startPointY + height;
//if transformation is defined, correct coordinates
if (ctm!=null)
{
//extract parameters
float a = ctm[0, 0];
float b = ctm[0, 1];
float c = ctm[1, 0];
float d = ctm[1, 1];
float e = ctm[2, 0];
float f = ctm[2, 1];
//reverse transformation to get x and y from x' and y'
startPointX = (startPointX - startPointY * c - e) / a;
startPointY = (startPointY - startPointX * b - f) / d;
endPointX = (endPointX - endPointY * c - e) / a;
endPointY = (endPointY - endPointX * b - f) / d;
}
rects.Add(new PdfRect(startPointX, startPointY , endPointX , endPointY ));
}
//store current ctm
else if (tokenizer.TokenType == PRTokeniser.TokType.OTHER && newBuf[newBuf.Count - 1] == "q")
{
if (ctm != null)
{
ctms.Add(ctm);
pendingCtm = false;
}
}
//fetch last ctm and remove it from list
else if (tokenizer.TokenType == PRTokeniser.TokType.OTHER && newBuf[newBuf.Count - 1] == "Q")
{
if (ctms.Count > 0)
{
ctm = ctms[ctms.Count - 1];
ctms.RemoveAt(ctms.Count -1 );
}
}
else if (tokenizer.TokenType == PRTokeniser.TokType.OTHER && newBuf[newBuf.Count - 1] == "cm")
{
// x' = x*a + y*c + e ; y' = x*b + y*d + f
float a = (float)double.Parse(newBuf[newBuf.Count - 7], format);
float b = (float)double.Parse(newBuf[newBuf.Count - 6], format);
float c = (float)double.Parse(newBuf[newBuf.Count - 5], format);
float d = (float)double.Parse(newBuf[newBuf.Count - 4], format);
float e = (float)double.Parse(newBuf[newBuf.Count - 3], format);
float f = (float)double.Parse(newBuf[newBuf.Count - 2], format);
float[,] tempCtm = ctm;
ctm = new float[3, 3] {
{a,b,0},
{c,d,0},
{e,f,1}
};
//multiply matrices to form 1 transformation matrix
if (pendingCtm && tempCtm != null)
{
float[,] resultantCtm;
if (!TryMultiplyMatrix(tempCtm, ctm, out resultantCtm))
{
throw new InvalidOperationException("Invalid transform matrix");
}
ctm = resultantCtm;
}
//current CTM has not yet been saved to stack
pendingCtm = true;
}
return rects;
}
The command you are looking for is cm. Did you read The ABC of PDF with iText? The book isn't finished yet, but you can already download the first five chapters.
This is a screen shot of the table that shows the cm operator:
This is an example of 5 shapes that are created in the exact same way, using identical syntax:
They are added at different positions, even in a different size and shape, because of the change in the graphics state: the coordinate system was changed, and the shapes are rendered in that altered coordinate system.