Last active
February 24, 2024 03:24
-
-
Save karnadii/53aae844b8fb110bf8651d261324e5e9 to your computer and use it in GitHub Desktop.
Crop a white border of an image
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ignore_for_file: avoid_print | |
// Import necessary libraries | |
import 'dart:io'; | |
import 'package:image/image.dart' as img; | |
// Main function | |
void main(List<String> args) async { | |
// Check if the path to the image file and the color of the border to remove are provided | |
if (args.length < 2) { | |
print("Please provide the path to the image file and the color of the border to remove (white or black)"); | |
return; | |
} | |
// Start the timer | |
final startTime = DateTime.now(); | |
// Read the image file | |
final String filePath = args[0]; | |
final String borderColor = args[1].toLowerCase(); | |
// Decode the image | |
img.Image? image; | |
img.Image? unfilteredImage; | |
await img.decodeImageFile(filePath).then((value) { | |
image = value; | |
}).catchError((e) { | |
print("Failed to decode image: $e"); | |
}); | |
await img.decodeImageFile(filePath).then((value) { | |
unfilteredImage = value; | |
}).catchError((e) { | |
print("Failed to decode image: $e"); | |
}); | |
// Set contrast and threshold values | |
final int contrast = args.length > 2 ? int.tryParse(args[2]) ?? 200 : 200; | |
final int threshold = args.length > 3 | |
? int.tryParse(args[3]) ?? (borderColor == 'white' ? 240 : 15) | |
: (borderColor == 'white' ? 240 : 15); | |
// Convert image to grayscale then increase contrast | |
final contrastedImage = img.contrast(img.grayscale(image!), contrast: contrast); | |
List<Future<int>> boundaryFutures = [ | |
Future<int>(() => findBoundary(contrastedImage, 'top', borderColor, threshold)), | |
Future<int>(() => findBoundary(contrastedImage, 'bottom', borderColor, threshold)), | |
Future<int>(() => findBoundary(contrastedImage, 'left', borderColor, threshold)), | |
Future<int>(() => findBoundary(contrastedImage, 'right', borderColor, threshold)), | |
]; | |
List<int> boundaries = await Future.wait(boundaryFutures); | |
final int top = boundaries[0]; | |
final int bottom = boundaries[1]; | |
final int left = boundaries[2]; | |
final int right = boundaries[3]; | |
// Crop the image | |
await cropAndSaveImage(unfilteredImage!, "./cropped_image.jpg", left, top, right - left, bottom - top); | |
// Print the time taken | |
final endTime = DateTime.now(); | |
print("Time taken: ${endTime.difference(startTime).inMilliseconds}ms"); | |
} | |
Future<img.Image> cropAndSaveImage(img.Image image, String path, int x, int y, int width, int height) async { | |
try { | |
// Check if the cropping parameters are within the image boundaries | |
if (x < 0 || y < 0 || x + width > image.width || y + height > image.height) { | |
throw ArgumentError('Cropping parameters are out of image boundaries.'); | |
} | |
// Crop the image | |
final croppedImage = img.copyCrop(image, x: x, y: y, width: width, height: height); | |
// Write the cropped image to a file | |
await File(path).writeAsBytes(img.encodeJpg(croppedImage)); | |
print('Image saved to $path'); | |
return croppedImage; | |
} catch (e) { | |
if (e is FileSystemException) { | |
print('Failed to write file: $e'); | |
} else { | |
print('An error occurred while cropping or saving the image: $e'); | |
} | |
rethrow; | |
} | |
} | |
int findBoundary(img.Image image, String direction, String borderColor, [int threshold = 240]) { | |
bool Function(img.Pixel) isBorderPixel; | |
if (borderColor == 'white') { | |
isBorderPixel = (img.Pixel pixel) => isWhite(pixel, threshold); | |
} else if (borderColor == 'black') { | |
isBorderPixel = (img.Pixel pixel) => isBlack(pixel, threshold); | |
} else { | |
throw ArgumentError('Invalid border color: $borderColor. Expected "white" or "black".'); | |
} | |
if (direction == 'top') { | |
for (int y = 0; y < image.height; y++) { | |
for (int x = 0; x < image.width; x++) { | |
if (!isBorderPixel(image.getPixel(x, y))) { | |
return y; | |
} | |
} | |
} | |
} else if (direction == 'bottom') { | |
for (int y = image.height - 1; y >= 0; y--) { | |
for (int x = 0; x < image.width; x++) { | |
if (!isBorderPixel(image.getPixel(x, y))) { | |
return y; | |
} | |
} | |
} | |
} else if (direction == 'left') { | |
for (int x = 0; x < image.width; x++) { | |
for (int y = 0; y < image.height; y++) { | |
if (!isBorderPixel(image.getPixel(x, y))) { | |
return x; | |
} | |
} | |
} | |
} else if (direction == 'right') { | |
for (int x = image.width - 1; x >= 0; x--) { | |
for (int y = 0; y < image.height; y++) { | |
if (!isBorderPixel(image.getPixel(x, y))) { | |
return x; | |
} | |
} | |
} | |
} | |
return 0; | |
} | |
// Function to check if a pixel is white | |
bool isWhite(img.Pixel pixel, [int threshold = 240]) { | |
final r = pixel.r; | |
final g = pixel.g; | |
final b = pixel.b; | |
// Check if the RGB values of the pixel are above the threshold | |
return r > threshold && g > threshold && b > threshold; | |
} | |
// Function to check if a pixel is black | |
bool isBlack(img.Pixel pixel, [int threshold = 15]) { | |
final r = pixel.r; | |
final g = pixel.g; | |
final b = pixel.b; | |
// Check if the RGB values of the pixel are below the threshold | |
return r < threshold && g < threshold && b < threshold; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment