Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions change.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ Version : 1.2.5

- ImageProcessingFragment
* Support for warmup frames
- Mean Blur
* Custom and concurrent function for the image border. Much faster on large images.

---------------------
Date : 2025/Sep/30
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, Peter Abeles. All Rights Reserved.
* Copyright (c) 2025, Peter Abeles. All Rights Reserved.
*
* This file is part of BoofCV (http://boofcv.org).
*
Expand Down Expand Up @@ -61,6 +61,7 @@ private void printPreamble() {
"import boofcv.struct.border.*;\n" +
"import boofcv.struct.convolve.*;\n" +
"import boofcv.struct.image.*;\n" +
"import pabeles.concurrency.GrowArray;\n" +
"\n" +
"import javax.annotation.Generated;\n" +
"\n" +
Expand Down Expand Up @@ -93,14 +94,15 @@ private void horizontal( String srcName , String dstName ) {
"\t\tif (BOverrideConvolveImageMean.invokeNativeHorizontal(input, output, offset, length))\n" +
"\t\t\treturn;\n" +
"\n" +
"\t\tKernel1D_"+suffix+" kernel = FactoryKernel.table1D_"+suffix+"(offset, length"+normalized+");\n" +
"\t\tif (length > input.width) {\n" +
"\t\t\tKernel1D_"+suffix+" kernel = FactoryKernel.table1D_"+suffix+"(offset, length"+normalized+");\n" +
"\t\t\tConvolveImageNormalized.horizontal(kernel, input, output);\n" +
"\t\t} else {\n" +
"\t\t\tConvolveNormalized_JustBorder_SB.horizontal(kernel, input, output);\n" +
"\t\t\tif (BoofConcurrency.USE_CONCURRENT) {\n" +
"\t\t\t\tImplConvolveMean_MT.horizontalBorder(input, output, offset, length);\n" +
"\t\t\t\tImplConvolveMean_MT.horizontal(input, output, offset, length);\n" +
"\t\t\t} else {\n" +
"\t\t\t\tImplConvolveMean.horizontalBorder(input, output, offset, length);\n" +
"\t\t\t\tImplConvolveMean.horizontal(input, output, offset, length);\n" +
"\t\t\t}\n" +
"\t\t}\n" +
Expand All @@ -127,14 +129,15 @@ private void vertical( String srcName , String dstName ) {
"\t\tif (BOverrideConvolveImageMean.invokeNativeVertical(input, output, offset, length))\n" +
"\t\t\treturn;\n" +
"\n" +
"\t\tKernel1D_"+suffix+" kernel = FactoryKernel.table1D_"+suffix+"(offset, length"+normalized+");\n" +
"\t\tif (length > input.height) {\n" +
"\t\t\tKernel1D_"+suffix+" kernel = FactoryKernel.table1D_"+suffix+"(offset, length"+normalized+");\n" +
"\t\t\tConvolveImageNormalized.vertical(kernel, input, output);\n" +
"\t\t} else {\n" +
"\t\t\tConvolveNormalized_JustBorder_SB.vertical(kernel, input, output);\n" +
"\t\t\tif (BoofConcurrency.USE_CONCURRENT) {\n" +
"\t\t\t\tImplConvolveMean_MT.verticalBorder(input, output, offset, length, workspaces);\n" +
"\t\t\t\tImplConvolveMean_MT.vertical(input, output, offset, length, workspaces);\n" +
"\t\t\t} else {\n" +
"\t\t\t\tImplConvolveMean.verticalBorder(input, output, offset, length, workspaces);\n" +
"\t\t\t\tImplConvolveMean.vertical(input, output, offset, length, workspaces);\n" +
"\t\t\t}\n" +
"\t\t}\n" +
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2023, Peter Abeles. All Rights Reserved.
* Copyright (c) 2025, Peter Abeles. All Rights Reserved.
*
* This file is part of BoofCV (http://boofcv.org).
*
Expand Down Expand Up @@ -39,35 +39,83 @@ public void generateCode() throws FileNotFoundException {
out.println("}");
}

public void addFunctions( AutoTypeImage imageIn , AutoTypeImage imageOut ) throws FileNotFoundException {
public void addFunctions( AutoTypeImage imageIn, AutoTypeImage imageOut ) throws FileNotFoundException {
this.imageIn = imageIn;
this.imageOut = imageOut;
printHorizontalBorder();
printHorizontal();
printVerticalBorder();
printVertical();
}

public void printPreamble() {
out.print(
"import boofcv.misc.BoofMiscOps;\n" +
"import boofcv.struct.image.*;\n" +
"import javax.annotation.Generated;\n" +
"import boofcv.concurrency.*;\n" +
"import org.ddogleg.struct.DogArray_F32;\n" +
"import org.ddogleg.struct.DogArray_F64;\n" +
"import org.ddogleg.struct.DogArray_I32;\n" +
"import org.jetbrains.annotations.Nullable;\n" +
"\n" +
"//CONCURRENT_INLINE import boofcv.concurrency.BoofConcurrency;\n");
"import boofcv.struct.image.*;\n" +
"import javax.annotation.Generated;\n" +
"import boofcv.concurrency.*;\n" +
"import org.ddogleg.struct.DogArray_F32;\n" +
"import org.ddogleg.struct.DogArray_F64;\n" +
"import org.ddogleg.struct.DogArray_I32;\n" +
"import org.jetbrains.annotations.Nullable;\n" +
"import pabeles.concurrency.GrowArray;\n" +
"\n" +
"//CONCURRENT_INLINE import boofcv.concurrency.BoofConcurrency;\n");

out.print(
"\n" +
"/**\n" +
" * <p>\n" +
" * Convolves a mean filter across the image. The mean value of all the pixels are computed inside the kernel.\n" +
" * </p>\n" +
generateDocString("Peter Abeles") +
"@SuppressWarnings({\"ForLoopReplaceableByForEach\",\"Duplicates\"})\n" +
"public class " + className + " {\n\n");
"/**\n" +
" * <p>\n" +
" * Convolves a mean filter across the image. The mean value of all the pixels are computed inside the kernel.\n" +
" * </p>\n" +
generateDocString("Peter Abeles") +
"@SuppressWarnings({\"ForLoopReplaceableByForEach\",\"Duplicates\"})\n" +
"public class " + className + " {\n\n");
}

public void printHorizontalBorder() {
String typeCast = imageOut.getTypeCastFromSum();
String sumType = imageIn.getSumType();
String bitWise = imageIn.getBitWise();

String divide = imageIn.isInteger() ? typeCast + "((total + count/2)/count)" : "total/count";

out.print("\tpublic static void horizontalBorder( " + imageIn.getSingleBandName() + " input, " + imageOut.getSingleBandName() + " output, int offset, int length ) {\n");
out.print("\t\tfinal " + imageIn.getDataType() + "[] dataSrc = input.data;\n" +
"\t\tfinal " + imageIn.getDataType() + "[] dataDst = output.data;\n" +
"\n" +
"\t\tfinal int offsetR = length - offset - 1;\n" +
"\n" +
"\t\tfinal int width = input.getWidth();\n" +
"\t\tfinal int height = input.getHeight();\n");

String body = "\t\t\tint indexDest = output.startIndex + y*output.stride;\n" +
"\t\t\tint j = input.startIndex + y*input.stride;\n" +
"\n" +
"\t\t\tfor (int i = 0; i < offset; i++) {\n" +
"\t\t\t\tint jEnd = j + i + length - offset;\n" +
"\t\t\t\t" + sumType + " total = 0;\n" +
"\t\t\t\tfor (int indexSrc = j; indexSrc < jEnd; indexSrc++) {\n" +
"\t\t\t\t\ttotal += dataSrc[indexSrc]" + bitWise + ";\n" +
"\t\t\t\t}\n" +
"\t\t\t\tint count = jEnd - j;\n" +
"\t\t\t\tdataDst[indexDest++] = " + divide + ";\n" +
"\t\t\t}\n" +
"\n" +
"\t\t\tint jEnd = j + width;\n" +
"\t\t\tj += width - (offset + offsetR);\n" +
"\t\t\tindexDest += width - (offset + offsetR);\n" +
"\t\t\tfor (int i = 0; i < offsetR; i++) {\n" +
"\t\t\t\t" + sumType + " total = 0;\n" +
"\t\t\t\tfor (int indexSrc = j + i; indexSrc < jEnd; indexSrc++) {\n" +
"\t\t\t\t\ttotal += dataSrc[indexSrc]" + bitWise + ";\n" +
"\t\t\t\t}\n" +
"\t\t\t\tint count = jEnd - j - i;\n" +
"\t\t\t\tdataDst[indexDest++] = " + divide + ";\n" +
"\t\t\t}\n";

printParallel("y", "0", "input.height", body);
out.print("\t}\n\n");
}

public void printHorizontal() {
Expand All @@ -79,7 +127,7 @@ public void printHorizontal() {
String declareHalf = imageIn.isInteger() ? "\t\tfinal " + sumType + " halfDivisor = divisor/2;\n" : "";
String divide = imageIn.isInteger() ? "(total+halfDivisor)/divisor" : "total/divisor";

out.print("\tpublic static void horizontal( " + imageIn.getSingleBandName() + " input ," + imageOut.getSingleBandName() + " output, int offset, int length ) {\n" +
out.print("\tpublic static void horizontal( " + imageIn.getSingleBandName() + " input," + imageOut.getSingleBandName() + " output, int offset, int length ) {\n" +
"\t\tfinal " + sumType + " divisor = length;\n" +
declareHalf);
String body = "";
Expand All @@ -94,38 +142,111 @@ public void printHorizontal() {
"\t\t\tfor (; indexIn < indexEnd; indexIn++) {\n" +
"\t\t\t\ttotal += input.data[indexIn] " + bitWise + ";\n" +
"\t\t\t}\n" +
"\t\t\toutput.data[indexOut++] = " + typeCast + "("+divide+");\n" +
"\t\t\toutput.data[indexOut++] = " + typeCast + "(" + divide + ");\n" +
"\n" +
"\t\t\tindexEnd = indexIn + input.width - length;\n" +
"\t\t\tfor (; indexIn < indexEnd; indexIn++) {\n" +
"\t\t\t\ttotal -= input.data[indexIn - length] " + bitWise + ";\n" +
"\t\t\t\ttotal += input.data[indexIn] " + bitWise + ";\n" +
"\n" +
"\t\t\t\toutput.data[indexOut++] = " + typeCast + "("+divide+");\n" +
"\t\t\t\toutput.data[indexOut++] = " + typeCast + "(" + divide + ");\n" +
"\t\t\t}\n";
printParallel("y","0","input.height",body);
printParallel("y", "0", "input.height", body);
out.print("\t}\n\n");
}

public void printVertical() {
public void printVerticalBorder() {
String typeCast = imageOut.getTypeCastFromSum();
String sumType = imageIn.getSumType();
String bitWise = imageIn.getBitWise();

String divide = imageIn.isInteger() ? typeCast + "((totals[x - x0] + count/2)/count)" : "totals[x - x0]/count";

String workType = ("DogArray_" + imageIn.getKernelType()).replace("S32", "I32");

out.print("\tpublic static void verticalBorder( " + imageIn.getSingleBandName() + " input, " + imageOut.getSingleBandName() + " output, " +
"int offset, int length, @Nullable GrowArray<" + workType + "> workspaces ) {\n");
out.print("\t\tworkspaces = BoofMiscOps.checkDeclare(workspaces, " + workType + "::new);\n" +
"\t\tfinal " + workType + " work = workspaces.grow(); //CONCURRENT_REMOVE_LINE\n" +
"\t\tfinal " + imageIn.getDataType() + "[] dataSrc = input.data;\n" +
"\t\tfinal " + imageIn.getDataType() + "[] dataDst = output.data;\n" +
"\n" +
"\t\tfinal int offsetR = length - offset - 1;\n" +
"\n" +
"\t\tfinal int width = input.getWidth();\n" +
"\t\tfinal int height = input.getHeight();\n");

String body = "";
body += "\t\t" + sumType + "[] totals = BoofMiscOps.checkDeclare(work, x1 - x0, false);\n" +
"\n" +
"\t\t// Image Top\n" +
"\t\tfor (int count = length - offset; count < length; count++) {\n" +
"\t\t\t{\n" +
"\t\t\t\tint indexIn = input.startIndex + x0;\n" +
"\n" +
"\t\t\t\tfor (int x = x0; x < x1; x++) {\n" +
"\t\t\t\t\ttotals[x - x0] = dataSrc[indexIn++]" + bitWise + ";\n" +
"\t\t\t\t}\n" +
"\t\t\t}\n" +
"\t\t\tfor (int y = 1; y < count; y++) {\n" +
"\t\t\t\tint indexIn = input.startIndex + x0 + y*input.stride;\n" +
"\n" +
"\t\t\t\tfor (int x = x0; x < x1; x++) {\n" +
"\t\t\t\t\ttotals[x - x0] += dataSrc[indexIn++]" + bitWise + ";\n" +
"\t\t\t\t}\n" +
"\t\t\t}\n" +
"\t\t\tint indexOut = output.startIndex + (count - (length - offset))*output.stride;\n" +
"\t\t\tfor (int x = x0; x < x1; x++) {\n" +
"\t\t\t\tdataDst[indexOut + x] = " + divide + ";\n" +
"\t\t\t}\n" +
"\t\t}\n" +
"\t\t// Image Bottom\n" +
"\t\tfor (int yStart = height - length + 1; yStart < height - offset; yStart++) {\n" +
"\t\t\t{\n" +
"\t\t\t\tint indexIn = input.startIndex + x0 + yStart*input.stride;\n" +
"\n" +
"\t\t\t\tfor (int x = x0; x < x1; x++) {\n" +
"\t\t\t\t\ttotals[x - x0] = dataSrc[indexIn++]" + bitWise + ";\n" +
"\t\t\t\t}\n" +
"\t\t\t}\n" +
"\n" +
"\t\t\tfor (int y = yStart + 1; y < height; y++) {\n" +
"\t\t\t\tint indexIn = input.startIndex + x0 + y*input.stride;\n" +
"\n" +
"\t\t\t\tfor (int x = x0; x < x1; x++) {\n" +
"\t\t\t\t\ttotals[x - x0] += dataSrc[indexIn++]" + bitWise + ";\n" +
"\t\t\t\t}\n" +
"\t\t\t}\n" +
"\n" +
"\t\t\tint count = height - yStart;\n" +
"\t\t\tint indexOut = output.startIndex + (yStart + offset)*output.stride;\n" +
"\t\t\tfor (int x = x0; x < x1; x++) {\n" +
"\t\t\t\tdataDst[indexOut + x] = " + divide + ";\n" +
"\t\t\t}\n" +
"\t\t}\n";

printParallelBlock("x0", "x1", "0", "input.width", "length", body);
out.print("\t}\n\n");
}

public void printVertical() {
String typeCast = imageOut.getTypeCastFromSum();
String sumType = imageIn.getSumType();
String bitWise = imageIn.getBitWise();

String declareHalf = imageIn.isInteger() ? "\t\tfinal " + sumType + " halfDivisor = divisor/2;\n" : "";
String divide = imageIn.isInteger() ? "(total + halfDivisor)/divisor" : "total/divisor";

String workType = ("DogArray_"+imageIn.getKernelType()).replace("S32","I32");
String workType = ("DogArray_" + imageIn.getKernelType()).replace("S32", "I32");

out.print("\tpublic static void vertical( "+imageIn.getSingleBandName()+" input, "+
imageOut.getSingleBandName()+" output, int offset, int length, @Nullable GrowArray<"+workType+"> workspaces ) {\n" +
"\t\tworkspaces = BoofMiscOps.checkDeclare(workspaces, "+workType+"::new);\n" +
"\t\tfinal "+workType+" work = workspaces.grow(); //CONCURRENT_REMOVE_LINE\n" +
out.print("\tpublic static void vertical( " + imageIn.getSingleBandName() + " input, " +
imageOut.getSingleBandName() + " output, int offset, int length, @Nullable GrowArray<" + workType + "> workspaces ) {\n" +
"\t\tworkspaces = BoofMiscOps.checkDeclare(workspaces, " + workType + "::new);\n" +
"\t\tfinal " + workType + " work = workspaces.grow(); //CONCURRENT_REMOVE_LINE\n" +
"\t\tfinal int backStep = length*input.stride;\n" +
"\t\tfinal int offsetEnd = length - offset - 1;\n" +
"\n" +
"\t\tfinal "+sumType+" divisor = length;\n" +
"\t\tfinal " + sumType + " divisor = length;\n" +
declareHalf +
"\n" +
"\t\t// To reduce cache misses it is processed along rows instead of going down columns, which is\n" +
Expand All @@ -134,18 +255,18 @@ public void printVertical() {

String body = "";

body += "\t\t"+sumType+"[] totals = BoofMiscOps.checkDeclare(work, input.width, false);\n" +
body += "\t\t" + sumType + "[] totals = BoofMiscOps.checkDeclare(work, input.width, false);\n" +
"\t\tfor (int x = 0; x < input.width; x++) {\n" +
"\t\t\tint indexIn = input.startIndex + (y0 - offset)*input.stride + x;\n" +
"\t\t\tint indexOut = output.startIndex + output.stride*y0 + x;\n" +
"\n" +
"\t\t\t"+sumType+" total = 0;\n" +
"\t\t\t" + sumType + " total = 0;\n" +
"\t\t\tint indexEnd = indexIn + input.stride*length;\n" +
"\t\t\tfor (; indexIn < indexEnd; indexIn += input.stride) {\n" +
"\t\t\t\ttotal += input.data[indexIn] "+bitWise+";\n" +
"\t\t\t\ttotal += input.data[indexIn] " + bitWise + ";\n" +
"\t\t\t}\n" +
"\t\t\ttotals[x] = total;\n" +
"\t\t\toutput.data[indexOut] = "+typeCast+"("+divide+");\n" +
"\t\t\toutput.data[indexOut] = " + typeCast + "(" + divide + ");\n" +
"\t\t}\n" +
"\n" +
"\t\t// change the order it is processed in to reduce cache misses\n" +
Expand All @@ -154,19 +275,19 @@ public void printVertical() {
"\t\t\tint indexOut = output.startIndex + y*output.stride;\n" +
"\n" +
"\t\t\tfor (int x = 0; x < input.width; x++, indexIn++, indexOut++) {\n" +
"\t\t\t\t"+sumType+" total = totals[x] - (input.data[indexIn - backStep]"+bitWise+");\n" +
"\t\t\t\ttotals[x] = total += input.data[indexIn]"+bitWise+";\n" +
"\t\t\t\t" + sumType + " total = totals[x] - (input.data[indexIn - backStep]" + bitWise + ");\n" +
"\t\t\t\ttotals[x] = total += input.data[indexIn]" + bitWise + ";\n" +
"\n" +
"\t\t\t\toutput.data[indexOut] = "+typeCast+"("+divide+");\n" +
"\t\t\t\toutput.data[indexOut] = " + typeCast + "(" + divide + ");\n" +
"\t\t\t}\n" +
"\t\t}\n";

printParallelBlock("y0","y1","offset","output.height - offsetEnd","length",body);
printParallelBlock("y0", "y1", "offset", "output.height - offsetEnd", "length", body);

out.print("\t}\n\n");
}

public static void main(String[] args) throws FileNotFoundException {
public static void main( String[] args ) throws FileNotFoundException {
var generator = new GenerateImplConvolveMean();
generator.setModuleName("boofcv-ip");
generator.generate();
Expand Down
Loading